I've read the Rust Book and some other tutorials on lifetimes. I thought I was understanding lifetimes as like a generic parameter that a function takes, and that how you assign it to the input and output parameters of your function constrains the lifetime of those parameters. So like fn find<'a, 'b>(pattern: &'a str, text: &'b str) -> &'b str means anything passed in as text needs to have a lifetime at least as long as the return value. But on a program I'm writing I got an error that makes me think I'm misunderstanding something fundamental about lifetimes.
I have a function with signature:
async fn check_git(requester: &mut Requester) -> Result<()>
Result is anyhow::Result if it matters.
When I compile I get the error:
error[E0726]: implicit elided lifetime not allowed here
--> src/scanner/git.rs:5:40
|
5 | pub async fn check_git(requester: &mut Requester) -> Result<()> {
| ^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
|
= note: assuming a `'static` lifetime...
I fixed the compiler error by adding a lifetime parameter: async fn check_git<'a>(requester: &mut Requester<'a>) -> Result<()>.
I understand requester can't have a static lifetime, as the way I'm calling check_git, the lifetime of the requester I pass in is the scope of the function calling check_git. But I don't understand exactly the meaning of the lifetime annotations I added.
My beginner understanding of lifetimes was that fn check_git<'a> is just specifying that 'a is a generic parameter, and then when you apply 'a to more than one parameter it constrains how the lifetimes of those parameters relate. But when I only apply 'a to one parameter there's no added constraint or meaning from the lifetime annotation?
The Requester struct I'm adding the lifetime parameter to is defined as:
struct Requester<'a> {
client: SendRequest<Body>,
resp: Option<Response<Body>>,
host: &'a str,
port: u16,
ctx: &'a SslContextRef,
}
But when I only apply 'a to one parameter there's no added constraint or meaning from the lifetime annotation?
Right. The rule here is not about "you should specify an additional constraint". Rather, it's that when you use a type that has a lifetime parameter, you should make it syntactically visible that there's a lifetime involved. For references, the & symbol itself is considered sufficient, but for named types, you're supposed to make the lifetime itself visible.
(It's still allowed to use hidden lifetime parameters in non-async functions, though I recommend prohibiting them with the lint configuration #![deny(rust_2018_idioms)].)
This doesn't mean you have to introduce an 'a though; you can specify '_, which means essentially “please use the lifetime elision rules” and thus, often but not always, “please use an anonymous distinct lifetime here”. So, write:
pub async fn check_git(requester: &mut Requester<'_>) -> Result<()> {
This follows the same rules as elided & lifetimes, so in this case it's the same as writing the elision-less form
pub async fn check_git<'a, 'b>(requester: &'a mut Requester<'b>) -> Result<()> {
Related
When I try to store closures to a HashMap, I come across a lifetime bound requirement reported by the compiler. It seems like an inconsistent requirement.
struct NoBox<C: Fn() -> ()>(HashMap<String, C>);
impl<C> NoBox<C>
where
C: Fn() -> (),
{
fn new() -> NoBox<C> {
NoBox(HashMap::new())
}
fn add(&mut self, str: &str, closure: C) {
self.0.insert(str.to_string(), closure);
}
}
This is Ok. The compiler is happy with it. However, when I try to wrap the closure into a trait object and store it. The compiler imposes a 'static lifetime bound on it.
struct Boxed(HashMap<String, Box<dyn Fn() -> ()>>);
impl Boxed {
fn new() -> Boxed {
Boxed(HashMap::new())
}
fn add<C>(&mut self, str: &str, closure: C)
where
C: Fn() -> ()//add 'static here fix the error
{
self.0.insert(str.to_string(), Box::new(closure)); //error: type parameter C may not live long enough, consider adding 'static lifebound
}
}
According to the complain of the compiler, C may not live long enough. It makes sense to add a 'static bound to it.
But, why the first case without boxing doesn't have this requirement?
To my understanding, if C contains some reference to an early-dropped referent, then store it in NoBox would also cause the invalid-reference problem. For me, it seems like an inconsistency.
NoBox is not a problem because if the function contains a reference to the lifetime, the type will stay contain this lifetime because the function type needs to be specified explicitly.
For example, suppose we're storing a closure that captures something with lifetime 'a. Then the closure's struct will looks like (this is not how the compiler actually desugars closures but is enough for the example):
struct Closure<'a> {
captured: &'a i32,
}
And when specifying it in NoBox, the type will be NoBox<Closure<'a>>, and so we know it cannot outlive 'a. Note this type may never be actually explicitly specified - especially with closures - but the compiler's inferred type still have the lifetime in it.
With Boxed on the other hand, we erase this information, and thus may accidentally outlive 'a - because it does not appear on the type. So the compiler enforces it to be 'static, unless you explicitly specify otherwise:
struct Boxed<'a>(HashMap<String, Box<dyn Fn() + 'a>>);
I'm writing a set of benchmarks for different key-value stores, and would like to have a single adapter trait that I can use in all the benchmarks, and then implement it for each key-value store.
This worked well for two of them. However, the third required me to add a lifetime on my trait, and after fighting the borrow checker for an entire day, I still can't seem to get it right.
I've distilled it down to this minimal repro: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54fec74cb70c63c03f25ec7a9dfc7e60
What I don't understand is why the borrow on txn lives longer than the scope of benchmark(). It seems to me that it should live for only that one line.
How can I define the AdapterTransaction trait to resolve this, that still allows implementations to choose their own return type?
edit
added that I need to be able to use the AdapterTransaction implementations with a factory trait
The main problem in your first playground is the lifetime on &self being the same as the generic lifetime on the trait.
pub trait AdapterTransaction<'a, T: AsRef<[u8]>> {
fn get(&'a self, key: &[u8]) -> Option<T>;
}
Because they are the same, it requires the borrow of the underlying type to live at least as long as the type itself. This isn't true because even though the type is owned, the borrow would only last for the duration of the function call. In benchmark<'a,...>(), the lifetime 'a is picked by the caller, and there is no way a borrow within that function can be long enough. There would have been a quick fix to remove the 'a parameter on benchmark and replace it with a higher ranked trait bound (playground).
fn benchmark<U: AsRef<[u8]>, T: for<'a> AdapterTransaction<'a, U>>(txn: T)
In this example, 'a isn't chosen by the caller anymore, so the compiler is free to use a valid lifetime for the call.
As for the 2nd part of your question, traits can define associated types which can change depending on the implementation. You could have a trait that has an associated Output, which can change for each implemented type. There is a big difference with doing a generic param vs an associated type since in the former case, you are allowed to implement multiple generic variants of a trait for the same type. (This is how From<T> works, for example).
pub trait AdapterTransaction<'a> {
type Output;
fn get(&'a self, key: &[u8]) -> Option<Self::Output>;
}
impl<'a> AdapterTransaction<'a> for AdapterImpl {
type Output = &'a [u8];
fn get(&'a self, key: &[u8]) -> Option<Self::Output> {
Some(self.txn.get(&key))
}
}
fn benchmark<T>(txn: T)
where
for<'a> T: AdapterTransaction<'a>,
{
let _ = txn.get(&[]).unwrap();
}
Edit: Some of my initial assumptions weren't exact, it's not necessary to implement on &'a Type if the trait lifetime is used in a non-conflicting way.
You can run the code snippet here. I removed a bunch of irrelevant functions and fields of the struct to make things simpler to analyze.
#[derive(Debug)]
pub struct Turtle<'a>{
children: Vec<&'a Turtle<'a>>,
}
impl Turtle<'_>{
pub fn new() -> Turtle<'static> {
Turtle {children: Vec::new()}
}
pub fn add_child<'a>(&self, t: &'a Turtle<'a>) {
&self.children.push(t);
}
}
The error is
|
11 | &self.children.push(t);
| ^ lifetime mismatch
|
= note: expected reference `&Turtle<'_>`
found reference `&Turtle<'a>`
note: the lifetime `'a` as defined on the method body at 10:22...
--> src/lib.rs:10:22
|
10 | pub fn add_child<'a>(&self, t: &'a Turtle<'a>) {
| ^^
note: ...does not necessarily outlive the lifetime `'_` as defined on the impl at 6:13
--> src/lib.rs:6:13
|
6 | impl Turtle<'_>{
| ^^
The problem appears to be that since impl has an anonymous lifetime, &self has type Turtle<'_>, so self.children has anonymous lifetime, meaning push expects a Turtle<'_>. I came up with 2 ideas to fix this, namely either changing impl to <'a> or the lifetime of parameter t to <'_>, but neither worked. I understand how to make sure the lifetime of a single function is correct, but when it comes to various functions, structs, and impls all connected together, things start to become unclear.
Your struct requires a lifetime, which you have chosen to call ‘a’. That lifetime is then ‘given to’ or ‘shared with‘ self.children. Later, you implement your struct for the anonymous lifetime, which is the lifetime that the struct definition states self.children must also share. However, your ‘new‘ function implies creation of self.children with a ‘static‘ lifetime (and ‘static != ‘_ as ‘_ could be shorter than ‘static). Further your add child method requires a (plausibly) entirely separate lifetime which you also call ‘a’ (just because you called it ‘a’ does not make it the same as the ‘a’ you named in the struct definition). Try creating a new struct that takes a type rather than a lifetime (say T or V, for example)? If neither have bounds then they will be essentially equal. The same applies to lifetimes.
The actual solution lies in matching your lifetimes. Because of the self-referential nature of your struct you cannot use the anonymous lifetime, as the compiler needs to know that the lifetimes match when adding children. What is below now compiles. Note the use of Self to infer the lifetime <'a> upon creation. You could also have used Turtle<'a> but I personally would consider using Self easier, as it avoids the additional lifetime reference. Also, in order for your add_child method to work, the reference to "self" needs to be mutable (see &mut self). This is because you are modifying the contents of self.children and therefore mutating the struct.
Note that this will work for "any" lifetime, whether you have called it 'a or 'z or anything else. The point is that the compiler can infer that the lifetimes of everything involved are matching.
#[derive(Debug)]
pub struct Turtle<'a> {
children: Vec<&'a Turtle<'a>>,
}
impl<'a> Turtle<'a> {
pub fn new() -> Self {
Self { children: Vec::new() }
}
pub fn add_child(&mut self, t: &'a Turtle<'a>) {
self.children.push(t);
}
}
I'm trying to create a type alias of a Peekable slice::Iter, but the compiler keeps complaining that I need a lifetime parameter.
This iterator is used in several places, and always iterates over the same type. To make the code more concise, I would like to use a type alias.
I have already tried (code examples below):
Adding a lifetime parameter to the struct it is iterating over, and pass this as the argument to the generic, to no avail.
Adding a lifetime parameter inside the iterator
use std::{iter::Peekable, slice::Iter};
pub struct MyStruct {
pub arg1: i32,
pub arg2: i32,
pub arg3: MyEnum,
}
pub enum MyEnum {
Default,
}
// mutable since I want to call .peek() and .next() on my iterator
type MyIterator<'a> = &'a mut Peekable<Iter<MyStruct>>;
Notice that the above works just fine if I don't use a type alias, i.e. I use it directly in code:
pub fn my_fn(it: &mut Peekable<Iter<MyStruct>>) -> i32 { /* ... */ }
I keep getting the following error:
error[E0106]: missing lifetime specifier
--> src/lib.rs:14:40
|
14 | type MyIterator<'a> = &'a mut Peekable<Iter<MyStruct>>;
| ^^^^^^^^^^^^^^ expected lifetime parameter
I have also tried:
... = &'a Peekable<Iter<&'a MyStruct>>;
... = &'a Peekable<Iter<MyStruct+ 'a>>;
Neither work, even when adding a lifetime parameter to the MyStruct. I don't really understand how Rust wants me to define the lifetime parameter. (In general I don't fully understand this concept yet as I just started programming in Rust)
I assume that Rust is just inferring the lifetime parameter for Iter in the function parameter context. But as E_net4 hinted at in their comment, the Iter you are probably using is defined as Iter<'a, T: 'a>. The correct type definition is:
type MyIterator<'a> = &'a mut Peekable<Iter<'a, MyStruct>>;
I have the following code in Rust. I know that I am not supposed to return references to local variables, and in this case I am not. The string to split is passed as a &str reference and, after determining the split boundary, I am returning &s[0..idx] where idx is the end of the boundary. I was confident that this would not result in a "dangling" reference related error. However, it turns out I was wrong!
fn demo4() {
let mut s = String::from("Elijah Wood");
let firstname = str_split(&s, &String::from(" "));
println!("First name of actor: {}", firstname);
}
// can handle both &str and &String
fn str_split(s: &str, pat: &str) -> &str {
let bytes = s.as_bytes();
let b_pat = pat.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b_pat {
return &s[0..i];
}
}
&s[..]
}
fn main() {
demo4();
}
I am getting the following error:
error[E0106]: missing lifetime specifier
--> src/main.rs:7:37
|
7 | fn str_split(s: &str, pat: &str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `pat`
Any explanation is greatly appreciated.
The error message tells you what's wrong, although not how to fix it:
= help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `pat`
The compiler uses lifetimes to determine whether code is safe or not. Part of that is knowing what each reference could be borrowing from. The signature:
fn str_split(s: &str, pat: &str) -> &str
does not indicate whether str_split returns a reference into s or a reference into pat, so Rust can't tell how to check the validity of the reference. (See also this question for a version of this where the function has no reference arguments at all.)
To fix this, you need to introduce a lifetime parameter:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str
This says, roughly, "If you borrow a string for some lifetime 'a, you can call str_split on it (and another string) and get back a reference also valid for lifetime 'a." &pat is not annotated with 'a, because the result does not borrow from pat, only from s.
The Rust Programming Language has a chapter on lifetimes that addresses this very issue and I would strongly recommend you read it; Rust's lifetimes go beyond merely preventing dangling pointers.
Although not part of the question, the the body of this function is a one-liner. Unless this is purely a learning exercise, don't do more work than you have to:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
s.split(pat).next().unwrap_or(s)
}
&str is a shorthand for &'a str, where 'a is some lifetime parameter that needs to be declared beforehand. In some simple cases. it's possible to omit these lifetime parameters and the compiler will expand it for you. However, there are some cases where you need to declare the lifetimes explicitly.
From The Rust Programming Language, Second Edition (emphasis mine), here are the rules regarding omitted lifetime parameters:
Each parameter that is a reference gets its own lifetime parameter. In other words, a function with one parameter gets one lifetime parameter: fn foo<'a>(x: &'a i32), a function with two arguments gets two separate lifetime parameters: fn foo<'a, 'b>(x: &'a i32, y: &'b i32), and so on.
If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32.
If there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, then the lifetime of self is assigned to all output lifetime parameters. This makes writing methods much nicer.
The problem with your function is that it has two input lifetime parameters, therefore the compiler will not choose one for you. You have to write your function like this:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
s
}
If this syntax is new to you, make sure you read the chapter on lifetimes.
Why can't the compiler just figure it out by itself? Because Rust has a principle that the signature of a function should not change because of a change in its implementation. It simplifies the compiler (it doesn't have to deal with interdependent functions whose signatures have not been fully determined) and it also simplifies the maintenance of your own code. For example, if you were to change the implementation of your function like so:
fn str_split(s: &str, pat: &str) -> &str {
pat
}
then the output's lifetime parameter would have to be linked to pat's lifetime parameter. In a library, this is a breaking change; you don't want breaking changes to slip by without you noticing!
Thanks to everyone for explaining the error and the reasons behind it. I have fixed the code and made some changes which I would like to explain. First thanks to #trentcl for noting that the pattern matching was semantically wrong. The reason being that the search was for the pattern was done by matching against each bytes in the array rather than the whole array itself. This prompted me to change the function to only return the words by splitting on the first occurrence of space character ' '.
Also the function signature needed a lifetime trait to be included to make to it compile correctly. The working code is presented below:
// 4 Demo with string spliting
fn demo4() {
let s = String::from("Elijah Wood");
let firstname = str_split(&s);
println!("First name of actor: {}", firstname);
}
// splits a string at first space
fn str_split<'a>(s : &'a str) -> &'a str {
let bytes = s.as_bytes();
for(i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}