1

While the code below is an early prototype and not to be taken too seriously concerning how I implement protocol buffers at this stage, I fail to understand what the error message rust compilers blesses me with refers to.

src\main.rs:89:9: 89:36 error: type mismatch resolving for<'r> <[closure src\ma in.rs:75:33: 88:10] as core::ops::FnOnce<(u32, gpb::definitions::WireType, &'r collections::vec::Vec<u8>, usize)>>::Output == usize: expected bound lifetime parameter , found concrete lifetime [E0271] src\main.rs:89 gpb::decoding::read_message( source, field_handler );

Even after reading the 3 documentation chapters on lifetimes et. al. I did not come across the term "concrete lifetime" and thus have trouble figuring out what code this error relates to. The closure itself, one or multiple arguments, the return code? The passing of the closure to read_message()? ...

main.rs snippet

fn from_gpb( source : &Vec<u8>) -> TimeMessage {
    fn init_vec_u64( count : usize, init_value : u64) -> Vec<u64> {
        let mut result = Vec::<u64>::with_capacity(count);
        for i in 0..count {
            result.push(init_value);
        }
        result
    }
    let mut message_id : u32 = 0;
    let mut times_sec  = init_vec_u64(4,0u64);
    let mut times_usec = init_vec_u64(4,0u64);
    let mut max_time_index = 0;
    let mut time_index = | index | { if max_time_index < index { max_time_index = index;}};
    let mut field_handler = |tag,wire_type,source,position| -> usize {
        match (tag,wire_type) {
            (1u32,gpb::definitions::WireType::Varint) => {let (v,p) = gpb::decoding::read_varint32(source,position); message_id = v; p},
            (2u32,gpb::definitions::WireType::Fixed64) => {let (sec,p) = gpb::decoding::read_fixed64(source,position); times_sec[0] = sec; time_index(0); p},
            (3u32,gpb::definitions::WireType::Fixed64) => {let (usec,p) = gpb::decoding::read_fixed64(source,position); times_usec[0] = usec; time_index(0); p},
            (4u32,gpb::definitions::WireType::Fixed64) => {let (sec,p) = gpb::decoding::read_fixed64(source,position); times_sec[1] = sec; time_index(1);p},
            (5u32,gpb::definitions::WireType::Fixed64) => {let (usec,p) = gpb::decoding::read_fixed64(source,position); times_usec[1] = usec; time_index(1);p},
            (6u32,gpb::definitions::WireType::Fixed64) => {let (sec,p) = gpb::decoding::read_fixed64(source,position); times_sec[2] = sec; time_index(2);p},
            (7u32,gpb::definitions::WireType::Fixed64) => {let (usec,p) = gpb::decoding::read_fixed64(source,position); times_usec[2] = usec; time_index(2); p},
            (8u32,gpb::definitions::WireType::Fixed64) => {let (sec,p) = gpb::decoding::read_fixed64(source,position); times_sec[3] = sec; time_index(3); p},
            (9u32,gpb::definitions::WireType::Fixed64) => {let (usec,p) = gpb::decoding::read_fixed64(source,position); times_usec[3] = usec; time_index(3); p},
            (_,_) => panic!("Invalid field tag/wire_type combination!") // TODO: change the panic to a gpb::decoding::skip(..) call.
        }
    };
    gpb::decoding::read_message( source, field_handler );
    let mut make_times = || -> Vec<prectime::PrecTime> {
        let time_count = max_time_index+1;
        let mut times = Vec::<prectime::PrecTime>::with_capacity(time_count);
        times_sec.truncate(time_count);
        times_usec.truncate(time_count);
        for i in 0..time_count {
            times.push(prectime::PrecTime { sec : times_sec[i], usec : times_usec[i]});
        }
        times               
    };
    TimeMessage { id : message_id, times : make_times() }    
}

gpb.rs snippet

pub fn read_message<F>( source : &Vec<u8>, field_handler : F) where F: Fn(u32,super::definitions::WireType, &Vec<u8>, usize) -> usize {
    let mut cursor = 0;
    while cursor < source.len() {
        let (tag_and_wire_type, position) = read_varint32( source, cursor );
        let wt = super::definitions::wire_type_from_value( tag_and_wire_type & 0x07u32 );
        let tag = (tag_and_wire_type >> 3);
        let new_pos = field_handler(tag,wt, source,position);
        cursor = new_pos;
    }
}

Summary of what the code does:

  • define field_handler function
  • read_message(data,field_handler) -> calls n times: field_handler
  • exit scope.

As anything (the closure, the calls, the vectors, the field_handler writes in from_gpb() context,...) is defined inside from_gpb() function, I simply do not understand how the lifetime could become an issue. All information about lifetimes should be available to the compiler.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • Slow today, guys, uh? :) I am also still clueless but I found https://github.com/rust-lang/rust/issues/26937 and to my not expert eyes, this looks as if it might be related. – BitTickler Jul 11 '15 at 23:49
  • Try annotating the closure like this: `|tag,wire_type,source: &_ ,position|` (you could also use the full type, but let's be ~~as cryptic~~ as lazy as possible.. full type is `&Vec`.) – bluss Jul 12 '15 at 00:10
  • Looks better but I still get: src\main.rs:89:9: 89:36 error: the trait `for<'r> core::ops::Fn<(u32, gpb::definitions::WireType, &'r collections::vec::Vec, usize)>` is not implemented for the type `[closure src\main.rs:75:33: 88:10]` [E0277] src\main.rs:89 gpb::decoding::read_message( source, field_handler ); – BitTickler Jul 12 '15 at 01:01

1 Answers1

10

First thing's first, if you want a quick answer, you should put some effort into writing a minimal, compilable example so that people don't have to guess whether or not a potential solution is going to work or not. Like this:

enum WireType {}

fn from_gpb(source: &Vec<u8>) {
    let mut field_handler = |tag, wire_type, source, position| -> usize {
        let tag: u32 = tag;
        let wire_type: WireType = wire_type;
        let source: &Vec<u8> = source;
        let position: usize = position;
        panic!();
    };
    read_message(source, field_handler);
}

fn read_message<F>(source: &Vec<u8>, field_handler: F)
where
    F: Fn(u32, WireType, &Vec<u8>, usize) -> usize,
{
    panic!();
}

The rest of this answer is based on the above which appears to replicate your problem:

error[E0631]: type mismatch in closure arguments
  --> src/lib.rs:11:5
   |
4  |     let mut field_handler = |tag, wire_type, source, position| -> usize {
   |                             ------------------------------------------- found signature of `fn(u32, WireType, &std::vec::Vec<u8>, usize) -> _`
...
11 |     read_message(source, field_handler);
   |     ^^^^^^^^^^^^ expected signature of `for<'r> fn(u32, WireType, &'r std::vec::Vec<u8>, usize) -> _`
   |
note: required by `read_message`
  --> src/lib.rs:14:1
   |
14 | / fn read_message<F>(source: &Vec<u8>, field_handler: F)
15 | | where
16 | |     F: Fn(u32, WireType, &Vec<u8>, usize) -> usize,
17 | | {
18 | |     panic!();
19 | | }
   | |_^

error[E0271]: type mismatch resolving `for<'r> <[closure@src/lib.rs:4:29: 10:6] as std::ops::FnOnce<(u32, WireType, &'r std::vec::Vec<u8>, usize)>>::Output == usize`
  --> src/lib.rs:11:5
   |
11 |     read_message(source, field_handler);
   |     ^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
   |
note: required by `read_message`
  --> src/lib.rs:14:1
   |
14 | / fn read_message<F>(source: &Vec<u8>, field_handler: F)
15 | | where
16 | |     F: Fn(u32, WireType, &Vec<u8>, usize) -> usize,
17 | | {
18 | |     panic!();
19 | | }
   | |_^

The simplest thing to do is to allow the compiler to properly infer the closure type:

fn from_gpb_closure_inference(source: &Vec<u8>) {
    read_message(source, |tag, wire_type, source, position| -> usize {
        let tag: u32 = tag;
        let wire_type: WireType = wire_type;
        let source: &Vec<u8> = source;
        let position: usize = position;
        panic!();
    });
}

Closure inference only really works properly when the closure is provided directly as an argument to a function. In theory, the two should be equivalent, but they aren't.

The other thing you can do is kind-of trick the compiler into doing the inference without actually using the closure:

fn constrain_handler<F>(f: F) -> F
where
    F: Fn(u32, WireType, &Vec<u8>, usize) -> usize,
{
    f
}

fn from_gpb_constrain(source: &Vec<u8>) {
    let mut field_handler = constrain_handler(|tag, wire_type, source, position| -> usize {
        let tag: u32 = tag;
        let wire_type: WireType = wire_type;
        let source: &Vec<u8> = source;
        let position: usize = position;
        panic!();
    });
    read_message(source, field_handler);
}

In this case, the constrain_handler function is just getting the compiler to nail down the type of the closure, allowing it to be used (or not) later on.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
DK.
  • 55,277
  • 5
  • 189
  • 162
  • on my read_message() I gave ``where F: Fn(u32, WireType, &Vec, usize) -> usize`` why does it need even more nailing down? – BitTickler Jul 12 '15 at 08:00
  • 1
    @BitTickler I can't give you a good explanation, because I don't understand it completely myself. I believe the issue is that closure inference is more complicated than regular inference, due to capturing, multiple `Fn*` traits, and higher-ranked lifetimes. This is the part that has (to date) never been fully implemented; it was implemented in argument position because it *had* to be in order for closures to be useful *at all*, but never went beyond that. For a better answer, you'll need to find someone who is familiar with the relevant part of the compiler. – DK. Jul 12 '15 at 08:35
  • 1
    The signature of the function is given in the verbatim. So it need not infer the types of the closure parameters as they are given (RUST has no implicit coercion,right?). What it needs to infer is the captures from the context used. And... in my code if I place the closure into the call to ``read_message`` it does NOT work - I get new errors such as ``cannot borrow data mutably in a captured outer variable in an `Fn` closure``. For now, I assume closures are broken in RUST and my humble opinion is they should not have used traits to implement them. (They would better be first class citizens) – BitTickler Jul 12 '15 at 19:22
  • @BitTickler: Actually, your *new* problem is because you specified a `Fn` closure when you want a `FnMut` closure. I missed that because I simplified the code a little too aggressively trying to get something that would compile. Closures aren't broken, they just aren't quite as flexible as they could be. – DK. Jul 13 '15 at 04:28
  • 1
    @DK. thanks a million, I never would've figured that out. Moving my closure definition from its own line to inside the call that was using it fixed my problem, just like you suggested. What I'm wondering now is, is there any way to accomplish the same thing with explicit type annotation? Like `let my_closure: [SOMETHING] = |...| {...};`? Or is putting the definition in a function call the only way? – Jack O'Connor Jan 26 '16 at 15:49
  • 1
    @JackO'Connor No, type annotations cannot help you here, because you need to constrain to a trait, not a type. The syntax for that doesn't exist. – DK. Jan 27 '16 at 02:10