An item signature is the bit which gives the name and types of your function, i.e. everything you need to call it (without needing to know how it's implemented); for example:
fn foo(x: u32) -> u32;
Here's another which takes a &str
reference:
fn bar<'a>(s: &'a str) -> &'a str;
In Rust, all references have an attached lifetime; this is part of the type. The above bar
function says more than just "this function takes a reference to a string and returns another one". It says "this function takes a string reference, and returns another which is valid for as long as the one it's given. This is an important part of Rust's ownership system.
However, it's annoying and a pain to specify these lifetimes every time, so Rust has "lifetime elision" (i.e. "not explicitly writing them out"). All that means is that for a few very common cases, you can leave the lifetime annotations out and Rust will implicitly add them for you. This is purely a convenience for programmers so that they don't have to write so many lifetimes in "obvious" cases.
The rules are listed in the book, but for completeness they are:
- Every lifetime in the function parameters which isn't otherwise specified is different. For example:
fn f(x: &T, y: &U)
means:
fn f<'a, 'b>(x: &'a T, y: &'b U)
i.e. there's no automatic link between those lifetimes.
- If there's only one input lifetime, it's used for every output lifetime. For example:
struct U<'a> {} // struct with a lifetime parameter
fn f(x: &T) -> &U
becomes:
fn f<'a>(x: &'a T) -> &'a U<'a>
- Otherwise, if there are multiple input lifetimes but one of them is
&self
or &mut self
(i.e. it's a method), then all the elided output lifetimes get the same as self
. This covers the common case that a method returns a reference to one of its fields. For example:
impl S {
fn get_my_item(&self, key: &str) -> &str {}
}
becomes:
fn get_my_item<'a,'b>(&'a self, key: &'b str) -> &'a str // use the self lifetime
The documentation has some more examples.