I have a very simple method. The first argument takes in vector components ("A", 5, 0) and I will compare this to every element of another vector to see if they have the same ( _ , 5 , _) and then print out the found element's string.
Comparing ("A", 5, 0 ) and ("Q", 5, 2) should print out Q.
fn is_same_space(x: &str, y1: i32, p: i32, vector: &Vec<(&str, i32, i32)>) -> (&str) {
let mut foundString = "";
for i in 0..vector.len() {
if y1 == vector[i].1 {
foundString = vector[i].0;
}
}
foundString
}
However, I get this error
error[E0106]: missing lifetime specifier
--> src/main.rs:1:80
|
1 | fn is_same_space(x: &str, y1: i32, p: i32, vector: &Vec<(&str, i32, i32)>) -> (&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 `x` or one of `vector`'s 2 elided lifetimes
By specifying a lifetime:
fn is_same_space<'a>(x: &'a str, y1: i32, p: i32, vector: &'a Vec<(&'a str, i32, i32)>) -> (&'a str)
This is only one of many possible interpretations of what you might have meant for the function to do, and as such it's a very conservative choice - it uses a unified lifetime of all the referenced parameters.
Perhaps you wanted to return a string that lives as long as x or as long as vector or as long as the strings inside vector; all of those are potentially valid.
I strongly recommend that you go back and re-read The Rust Programming Language. It's free, and aimed at beginners to Rust, and it covers all the things that make Rust unique and are new to programmers. Many people have spent a lot of time on this book and it answers many beginner questions such as this one.
Specifically, you should read the chapters on:
ownership
references and borrowing
lifetimes
There's even a second edition in the works, with chapters like:
Understanding Ownership
Generic Types, Traits, and Lifetimes
For fun, I'd rewrite your code using iterators:
fn is_same_space<'a>(y1: i32, vector: &[(&'a str, i32, i32)]) -> &'a str {
vector.iter()
.rev() // start from the end
.filter(|item| item.1 == y1) // element that matches
.map(|item| item.0) // first element of the tuple
.next() // take the first (from the end)
.unwrap_or("") // Use a default value
}
Removed the unneeded parameters.
Using an iterator avoids the overhead of bounds checks, and more clearly exposes your intent.
Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument?
Rust does not use camelCase variable names.
I assume that you do want to return the string from inside vector.
Remove the redundant parens on the return type
So the problem comes from the fact that vector has two inferred lifetimes, one for vector itself (the &Vec part) and one for the &str inside the vector. You also have an inferred lifetime on x, but that really inconsequential.
To fix it, just specify that the returned &str lives as long as the &str in the vector:
fn is_same_space<'a>( // Must declare the lifetime here
x: &str, // This borrow doesn't have to be related (x isn't even used)
y1: i32, // Not borrowed
p: i32, // Not borrowed or used
vector: &'a Vec<(&'a str, i32, i32)> // Vector and some of its data are borrowed here
) -> &'a str { // This tells rustc how long the return value should live
...
}
Related
I can't call Foo::new(words).split_first() in the following code
fn main() {
let words = "Sometimes think, the greatest sorrow than older";
/*
let foo = Foo::new(words);
let first = foo.split_first();
*/
let first = Foo::new(words).split_first();
println!("{}", first);
}
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn split_first(&'a self) -> &'a str {
self.part.split(',').next().expect("Could not find a ','")
}
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
the compiler will give me an error message
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:8:17
|
8 | let first = Foo::new(words).split_first();
| ^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
9 |
10 | println!("{}", first);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
If I bind the value of Foo::new(words) first, then call the split_first method there is no problem.
These two methods of calling should intuitively be the same but are somehow different.
Short answer: remove the 'a lifetime for the self parameter of split_first: fn split_first(&self) -> &'a str (playground).
Long answer:
When you write this code:
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
You are telling the compiler that all Foo instances are related to some lifetime 'a that must be equal to or shorter than the lifetime of the string passed as parameter to Foo::new. That lifetime 'a may be different from the lifetime of each Foo instance. When you then write:
let words = "Sometimes think, the greatest sorrow than older";
Foo::new(words)
The compiler infers that the lifetime 'a must be equal to or shorter than the lifetime of words. Barring any other constraints the compiler will use the lifetime of words, which is 'static so it is valid for the full life of the program.
When you add your definition of split_first:
fn split_first(&'a self) -> &'a str
You are adding an extra constraint: you are saying that 'a must also be equal to or shorter than the lifetime of self. The compiler will therefore take the shorter of the lifetime of words and the lifetime of the temporary Foo instance, which is the lifetime of the temporary. #AndersKaseorg's answer explains why that doesn't work.
By removing the 'a lifetime on the self parameter, I am decorrelating 'a from the lifetime of the temporary, so the compiler can again infer that 'a is the lifetime of words, which is long enough for the program to work.
Foo::new(words).split_first() would be interpreted roughly as
let tmp = Foo::new(words);
let ret = tmp.split_first();
drop(tmp);
ret
If Rust allowed you to do this, the references in ret would point [edit: would be allowed by the type of split_first to point*] into the now dropped value of tmp. So it’s a good thing that Rust disallows this. If you wrote the equivalent one-liner in C++, you’d silently get undefined behavior.
By writing the let binding yourself, you delay the drop until the end of the scope, thus extending the region where it’s safe to have these references.
For more details, see temporary lifetimes in the Rust Reference.
* Edit: As pointed out by Jmb, the real problem in this particular example is that the type
fn split_first(&'a self) -> &'a str
isn’t specific enough, and a better solution is to refine the type to:
fn split_first<'b>(&'b self) -> &'a str
which can be abbreviated:
fn split_first(&self) -> &'a str
This conveys the intended guarantee that the returned references do not point into the Foo<'a> (only into the string itself).
This code fails the dreaded borrow checker (playground):
struct Data {
a: i32,
b: i32,
c: i32,
}
impl Data {
fn reference_to_a(&mut self) -> &i32 {
self.c = 1;
&self.a
}
fn get_b(&self) -> i32 {
self.b
}
}
fn main() {
let mut dat = Data{ a: 1, b: 2, c: 3 };
let aref = dat.reference_to_a();
println!("{}", dat.get_b());
}
Since non-lexical lifetimes were implemented, this is required to trigger the error:
fn main() {
let mut dat = Data { a: 1, b: 2, c: 3 };
let aref = dat.reference_to_a();
let b = dat.get_b();
println!("{:?}, {}", aref, b);
}
Error:
error[E0502]: cannot borrow `dat` as immutable because it is also borrowed as mutable
--> <anon>:19:20
|
18 | let aref = dat.reference_to_a();
| --- mutable borrow occurs here
19 | println!("{}", dat.get_b());
| ^^^ immutable borrow occurs here
20 | }
| - mutable borrow ends here
Why is this? I would have thought that the mutable borrow of dat is converted into an immutable one when reference_to_a() returns, because that function only returns an immutable reference. Is the borrow checker just not clever enough yet? Is this planned? Is there a way around it?
Lifetimes are separate from whether a reference is mutable or not. Working through the code:
fn reference_to_a(&mut self) -> &i32
Although the lifetimes have been elided, this is equivalent to:
fn reference_to_a<'a>(&'a mut self) -> &'a i32
i.e. the input and output lifetimes are the same. That's the only way to assign lifetimes to a function like this (unless it returned an &'static reference to global data), since you can't make up the output lifetime from nothing.
That means that if you keep the return value alive by saving it in a variable, you're keeping the &mut self alive too.
Another way of thinking about it is that the &i32 is a sub-borrow of &mut self, so is only valid until that expires.
As #aSpex points out, this is covered in the nomicon.
Why is this an error: While a more precise explanation was already given by #Chris some 2.5 years ago, you can read fn reference_to_a(&mut self) -> &i32 as a declaration that:
“I want to exclusively borrow self, then return a shared/immutable reference which lives as long as the original exclusive borrow” (source)
Apparently it can even prevent me from shooting myself in the foot.
Is the borrow checker just not clever enough yet? Is this planned?
There's still no way to express "I want to exclusively borrow self for the duration of the call, and return a shared reference with a separate lifetime". It is mentioned in the nomicon as #aSpex pointed out, and is listed among the Things Rust doesn’t let you do as of late 2018.
I couldn't find specific plans to tackle this, as previously other borrow checker improvements were deemed higher priority. The idea about allowing separate read/write "lifetime roles" (Ref2<'r, 'w>) was mentioned in the NLL RFC, but no-one has made it into an RFC of its own, as far as I can see.
Is there a way around it? Not really, but depending on the reason you needed this in the first place, other ways of structuring the code may be appropriate:
You can return a copy/clone instead of the reference
Sometimes you can split a fn(&mut self) -> &T into two, one taking &mut self and another returning &T, as suggested by #Chris here
As is often the case in Rust, rearranging your structs to be "data-oriented" rather than "object-oriented" can help
You can return a shared reference from the method: fn(&mut self) -> (&Self, &T) (from this answer)
You can make the fn take a shared &self reference and use interior mutability (i.e. define the parts of Self that need to be mutated as Cell<T> or RefCell<T>). This may feel like cheating, but it's actually appropriate, e.g. when the reason you need mutability as an implementation detail of a logically-immutable method. After all we're making a method take a &mut self not because it mutates parts of self, but to make it known to the caller so that it's possible to reason about which values can change in a complex program.
This code fails the dreaded borrow checker (playground):
struct Data {
a: i32,
b: i32,
c: i32,
}
impl Data {
fn reference_to_a(&mut self) -> &i32 {
self.c = 1;
&self.a
}
fn get_b(&self) -> i32 {
self.b
}
}
fn main() {
let mut dat = Data{ a: 1, b: 2, c: 3 };
let aref = dat.reference_to_a();
println!("{}", dat.get_b());
}
Since non-lexical lifetimes were implemented, this is required to trigger the error:
fn main() {
let mut dat = Data { a: 1, b: 2, c: 3 };
let aref = dat.reference_to_a();
let b = dat.get_b();
println!("{:?}, {}", aref, b);
}
Error:
error[E0502]: cannot borrow `dat` as immutable because it is also borrowed as mutable
--> <anon>:19:20
|
18 | let aref = dat.reference_to_a();
| --- mutable borrow occurs here
19 | println!("{}", dat.get_b());
| ^^^ immutable borrow occurs here
20 | }
| - mutable borrow ends here
Why is this? I would have thought that the mutable borrow of dat is converted into an immutable one when reference_to_a() returns, because that function only returns an immutable reference. Is the borrow checker just not clever enough yet? Is this planned? Is there a way around it?
Lifetimes are separate from whether a reference is mutable or not. Working through the code:
fn reference_to_a(&mut self) -> &i32
Although the lifetimes have been elided, this is equivalent to:
fn reference_to_a<'a>(&'a mut self) -> &'a i32
i.e. the input and output lifetimes are the same. That's the only way to assign lifetimes to a function like this (unless it returned an &'static reference to global data), since you can't make up the output lifetime from nothing.
That means that if you keep the return value alive by saving it in a variable, you're keeping the &mut self alive too.
Another way of thinking about it is that the &i32 is a sub-borrow of &mut self, so is only valid until that expires.
As #aSpex points out, this is covered in the nomicon.
Why is this an error: While a more precise explanation was already given by #Chris some 2.5 years ago, you can read fn reference_to_a(&mut self) -> &i32 as a declaration that:
“I want to exclusively borrow self, then return a shared/immutable reference which lives as long as the original exclusive borrow” (source)
Apparently it can even prevent me from shooting myself in the foot.
Is the borrow checker just not clever enough yet? Is this planned?
There's still no way to express "I want to exclusively borrow self for the duration of the call, and return a shared reference with a separate lifetime". It is mentioned in the nomicon as #aSpex pointed out, and is listed among the Things Rust doesn’t let you do as of late 2018.
I couldn't find specific plans to tackle this, as previously other borrow checker improvements were deemed higher priority. The idea about allowing separate read/write "lifetime roles" (Ref2<'r, 'w>) was mentioned in the NLL RFC, but no-one has made it into an RFC of its own, as far as I can see.
Is there a way around it? Not really, but depending on the reason you needed this in the first place, other ways of structuring the code may be appropriate:
You can return a copy/clone instead of the reference
Sometimes you can split a fn(&mut self) -> &T into two, one taking &mut self and another returning &T, as suggested by #Chris here
As is often the case in Rust, rearranging your structs to be "data-oriented" rather than "object-oriented" can help
You can return a shared reference from the method: fn(&mut self) -> (&Self, &T) (from this answer)
You can make the fn take a shared &self reference and use interior mutability (i.e. define the parts of Self that need to be mutated as Cell<T> or RefCell<T>). This may feel like cheating, but it's actually appropriate, e.g. when the reason you need mutability as an implementation detail of a logically-immutable method. After all we're making a method take a &mut self not because it mutates parts of self, but to make it known to the caller so that it's possible to reason about which values can change in a complex program.
In the chapter Lifetimes of the Rust book, there's an example:
struct Foo<'a> {
x: &'a i32,
}
fn main() {
let y = &5; // this is the same as `let _y = 5; let y = &_y;`
let f = Foo { x: y };
println!("{}", f.x);
}
Why do they use x: &'a i32?
I think if it is just x: i32 then they cannot demonstrate the lifetime usage. However, is there any other reason behind it? Is there any production code that uses immutable reference to a primitive type like i32?
In this particular case the reason is indeed to show the concept of lifetimes. As for the general case, however, I see no reason making an immutable reference to a primitive type (mutable references, of course, is another matter) except of when it is done in generic code:
struct Holder<'a, T> {
r: &'a T
}
let x: i32 = 123;
let h: Holder<i32> = Holder { r: &x };
Here if you have such structure, you have no other choice as to use a reference to an i32. Naturally, this structure can also be used with other, non-primitive and non-movable types.
As Shepmaster has mentioned in comments, there is indeed a case where you have references to primitive types - it is by-reference iterators. Remember, by a convention (which the standard library follows) iter() method on a collection should return an iterator of references into the collection:
let v: Vec<i32> = vec![1, 2, 3, 4];
let i = v.iter(); // i is Iterator<Item=&i32>
Then almost all methods on the iterator which take a closure will accept closures whose argument is a reference:
i.map(|n| *n + 1) // n is of type &i32
Note that this is in fact a consequence of the more general case with generics. Vectors and slices may contain arbitrary types, including non-moveable ones, so they just have to have methods which would allow their users to borrow their contents.
I'm trying to return a slice from a vector which is built inside my function. Obviously this doesn't work because v's lifetime expires too soon. I'm wondering if there's a way to extend v's lifetime. I want to return a plain slice, not a vector.
pub fn find<'a>(&'a self, name: &str) -> &'a[&'a Element] {
let v: Vec<&'a Element> = self.iter_elements().filter(|&elem| elem.name.borrow().local_name == name).collect();
v.as_slice()
}
You can't forcibly extend a value's lifetime; you just have to return the full Vec. If I may ask, why do you want to return the slice itself? It is almost always unnecessary, since a Vec can be cheaply (both in the sense of easy syntax and low-overhead at runtime) coerced to a slice.
Alternatively, you could return the iterator:
use std::iter;
pub fn find<'a>(&'a self, name: &str) -> Box<Iterator<Item = &'a Element> + 'a> {
Box::new(self.iter_elements()
.filter(move |&elem| elem.name.borrow().local_name == name))
}
For now, you will have to use an iterator trait object, since closure have types that are unnameable.
NB. I had to change the filter closure to capture-by-move (the move keyword) to ensure that it can be returned, or else the name variable would just passed into the closure pointer into find's stack frame, and hence would be restricted from leaving find.