A commented example to illustrate some concepts:
use rand;
#[derive(Debug)]
struct Struct {
id: i32
}
impl Drop for Struct {
fn drop(&mut self) {
// print when struct is dropped
println!("dropped {:?}", self);
}
}
fn rand_struct() -> Struct {
Struct { id: rand::random() }
}
/*
this does not compile:
fn rand_struct_ref<'a>() -> &'a Struct {
&rand_struct()
// struct dropped here so we can't return it to caller's scope
}
*/
fn takes_struct_ref(s: &Struct) {}
fn main() {
// brings struct into scope and immediately creates ref to it
// this is okay because the struct is not dropped until end of this scope
let s = &rand_struct();
println!("holding ref to {:?}", s);
// all these work because of deref coercion
takes_struct_ref(s);
takes_struct_ref(&s);
takes_struct_ref(&&s);
// struct dropped here
}
playground
To answer your first question: You can't return a reference from a function if the underlying value is dropped at the end of the function but it's okay to immediately make a reference to a returned value since that value lives for the rest of the caller's scope. You can see this in action if you run the above example, as holding ref to Struct { id: <id> }
will get printed before dropped Struct { id: <id> }
.
To answer your second question: the reason you can pass an &Struct
or an &&Struct
or an &&&Struct
and so on to function which only takes an &Struct
is because of a Rust syntax sugar feature called deref coercion which automatically derefs variables when they are used as function arguments or as part of method calls. In your shared code examples it looks like a ref of a ref is being passed to the function but actually it's just passing a single ref after auto deref coercion takes place.
See also