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