rust - Code working with elided lifetimes, not with explicit -
the following code works fine:
fn get<f: fn(&[u8]) -> u8>(f: f) -> u8 { f(&[1, 2, 3]) } however, when add explicit lifetime information it, doesn't:
fn get<'inp, f: fn(&'inp [u8]) -> u8>(f: f) -> u8 { f(&[1, 2, 3]) } what lifetime compiler infer in working code?
i'm using rust 1.18.0.
the error message is:
error: borrowed value not live long enough --> test.rs:4:8 | 4 | f(&[1, 2, 3]) | ^^^^^^^^^ not live long enough 5 | } | - temporary value lives until here | note: borrowed value must valid lifetime 'inp defined on body @ 3:49... --> test.rs:3:50 | 3 | fn get<'inp, f: fn(&'inp [u8]) -> u8>(f: f) -> u8 { | __________________________________________________^ 4 | | f(&[1, 2, 3]) 5 | | } | |_^
lifetimes in trait bounds bit special , fn family of traits has special lifetime elision rule. we'll dive that, first, here correct explicitly annotated version:
fn get<f: for<'inp> fn(&'inp [u8]) -> u8>(f: f) -> u8 { f(&[1, 2, 3]) } oh gosh, for<'inp> doing there? it's called higher ranked trait bound (hrtb) , it's used here make 'inp universally quantiefied in regards f. in order understand that, need understand bit of theory.
who has choice?
let's take @ easy example:
fn bar<'a>(x: &'a u8) {} here, bar() generic of lifetime 'a. syntax above reads: "choose 'a , there bar() work 'a". means can choose 'a want, , bar() works! "we"? "we" caller -- 1 calling bar. important later: caller chooses generic parameters. can call bar() &'static u8 reference doesn't live long.
now might ask: there situations caller doesn't choose generic parameter, else does? yes, there are! sadly, it's bit more difficult understand, because doesn't occur in today's rust code. let's try:
trait bar<'a> { fn bar(&self, x: &'a u8); } this similar bar() function above, lifetime parameter defined on trait, not function. let's use trait:
fn use_bar<'a, b: bar<'a>>(b: b) { let local = 0u8; b.bar(&local); } this doesn't compile, printing same error above. why? method b.bar() expects reference of lifetime 'a. chooses 'a here? exactly: caller -- caller of use_bar(), not caller of bar()! caller of use_bar() choose 'static lifetime; in case, it's easy see our &local doesn't fulfill lifetime requirements.
note caller of use_bar() chooses 'a b. once use_bar() instantiated, b fixed type , b::bar() works 1 specific lifetime. means caller of bar() can't choose lifetime, bar() chose it!
what want instead? want use_bar() choose lifetime of bar() call. , can for<> syntax:
fn use_bar<b: for<'a> bar<'a>>(b: b) { let local = 0u8; b.bar(&local); } this works. here is: "for lifetime parameter 'a, b has implement trait bar<'a>". instead of: "there needs exist lifetime parameter 'a b implements bar<'a>". it's chooses parameter.
let's use real names it:
- a generic parameter universally quantified if caller can choose it
- a generic parameter existentially quantified if callee can choose it
what rust do?
to return example:
fn get<'inp, f: fn(&'inp [u8]) -> u8>(f: f) -> u8 { f(&[1, 2, 3]) } here have same problem above: lifetime parameter of f existentially quantified. caller of f cannot choose lifetime parameter. can fix for<> syntax shown above.
when omit lifetimes:
fn get<f: fn(&[u8]) -> u8>(f: f) -> u8 { f(&[1, 2, 3]) } the rust compiler special fn family of traits. f: fn(&[u8]) desugars f: for<'a> fn<(&'a [u8],)>. if use fn* traits parameters involve lifetimes, lifetimes automatically universally quantified, because that's want higher order functions.
Comments
Post a Comment