I'm trying to iterate through a String via bytes iterator. My intent is to convert each byte into a single-letter &str which gets pattern matched. Based on the match arm, it will attempt to add the value to a Vector. However, I've been getting an error which is preventing the program to compile. The error message is this:
Line 15, Char 22: temporary value dropped while borrowed (solution.rs)
|
15 | let b = &[byte];
| ^^^^^^ creates a temporary which is freed while still in use
...
20 | stack.push(c);
| ------------- borrow later used here
...
39 | }
| - temporary value is freed at the end of this statement
|
= note: consider using a `let` binding to create a longer lived value
For more information about this error, try `rustc --explain E0716`.
error: could not compile `prog` due to previous error
For reference, this is the underlying piece of code:
let mut stack: Vec<&str> = Vec::new();
for byte in s.bytes() {
let b = &[byte];
let c = str::from_utf8(b).unwrap();
match c {
"a" | "b" => {
stack.push(c);
},
_ => println!("not a or b"),
}
}
The problem is that you create a temporary array, [byte], and try to push a borrow of it into the Vec. But the array lives only to the end of the scope - and the Vec lives longer!
The usual solution will be to heap-allocate the &str: stack.push(c.to_owned()). But because the memory layout of one-element array matches that of the element, you can go without allocating. First, you need to get a reference to byte with the same lifetime as the original &str instead of a by-value byte. This can be done by iterating over s.as_bytes() (that returns &[u8]) instead of over s.bytes() (that returns impl Iterator<Item = u8>).
Second, you need to convert this byte into an array with the same lifetime. There is a function in the standard library to do that: std::array::from_ref().
So:
let mut stack: Vec<&str> = Vec::new();
for byte in s.as_bytes() {
let b = std::array::from_ref(byte);
let c = str::from_utf8(b).unwrap();
match c {
"a" | "b" => {
stack.push(c);
}
_ => println!("not a or b"),
}
}
Related
I am newbie in the Rust world.
As an exercise, this is the problem I am trying to solve:
fn main() {
let s = give_ownership();
println!("{}", s);
}
// Only modify the code below!
fn give_ownership() -> String {
let s = String::from("hello, world");
// Convert String to Vec
let _s = s.into_bytes();
s
}
I have gotten through. My solution works.
However, when I compile the exercise code-snippet above unchanged, I don't quite get what the compiler is telling me here, as a note below:
Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `s`
--> src/main.rs:12:5
|
9 | let s = String::from("hello, world");
| - move occurs because `s` has type `String`, which does not implement the `Copy` trait
10 | // Convert String to Vec
11 | let _s = s.into_bytes();
| ------------ `s` moved due to this method call
12 | s
| ^ value used here after move
|
note: this function takes ownership of the receiver `self`, which moves `s`
My guess is that the note is about the function into_bytes(). The RustDoc says this about the function:
This consumes the String, so we do not need to copy its contents.
Could someone please elaborate on this?
into_bytes() takes self (i.e. an owned self, not a reference).
This means that it takes ownership of the string it's called on. It's conceptually the same as this:
fn main() {
let s = String::from("hello");
take_string(s);
println!("{s}"); // ERROR
}
fn take_string(s: String) {}
This is useful because it allows you to turn a String into a Vec<u8>, while reusing the allocation. A String is really just a Vec<u8> with the guarantee that the bytes are valid UTF-8.
So once you write let _s = s.into_bytes(), the data that was in s has now moved to _s, so you can't return s from your function. There's nothing there.
If you just want to return the string, you can just return String::from("stuff")
What I am trying to do
I have in iterator returned from std::str::SplitWhitespace, I need the first element, and a vector of all elements.
What I have tried
I tried to use peek. However this seems to need a mutable (I can't we why), and I end up with borrowing errors.
Simplified code, with compile errors.
fn main(){
let line = "hello, world";
let mut tokens = line.split_whitespace().peekable();
if let Some(first) = tokens.peek() {
//println!("{first}"); //works
//println!("{tokens:?}"); // works
println!("{first}\n{tokens:?}"); //compile error
}
}
error[E0502]: cannot borrow `tokens` as immutable because it is also borrowed as mutable
--> src/main.rs:7:29
|
4 | if let Some(first) = tokens.peek() {
| ------------- mutable borrow occurs here
...
7 | println!("{first}\n{tokens:?}"); //error
| --------------------^^^^^^-----
| | |
| | immutable borrow occurs here
| mutable borrow later used here
If I un-comment the two printlns, and comment the erroneous one. then It works. This lead me to adding a clone let first = first.clone();, before the printlns. This fixed it, but I am wondering if there is a better way.
The simplest way to achieve this is to just collect the vector, then get the first element:
let tokens: Vec<_> = line.split_whitespace().collect();
if let Some(first) = tokens.first() {
println!("{first}\n{tokens:?}");
}
Note the type of tokens will be Vec<&str> -- a vector of string slices that are borrowed from line. If you instead want a vector of owned strings (Vec<String>), then you need to map each element to an owned string:
let tokens: Vec<_> = line.split_whitespace().map(|s| s.to_owned()).collect();
I just started to learn Rust and I'm trying to compare two versions of type String. As the Version::from function only accepts &str, I'm trying to convert, but then I get the error that tag does not live long enough. I understand that it goes out of scope, but how to fix it?
use version_compare::Version;
fn main() {
let tags: Vec<String> = vec!["1.2.1".to_string(),"1.2.2".to_string()];
let mut max_ver = Version::from("0.0.0").unwrap();
for tag in tags {
let v_tag = Version::from(&tag.as_str()).unwrap();
if v_tag > max_ver {
max_ver = v_tag;
}
}
println!("max_ver: {max_ver}");
}
Error:
Compiling rust-test2 v0.1.0 (/Users/scenox/rust-test2)
error[E0597]: `tag` does not live long enough
--> src/main.rs:8:36
|
8 | let v_tag = Version::from(&tag.as_str()).unwrap();
| ^^^^^^^^^^^^ borrowed value does not live long enough
9 | if v_tag > max_ver {
| ------- borrow later used here
...
12 | }
| - `tag` dropped here while still borrowed
The problem here is that the for loop consumes tags, meaning that each String is assigned to tag one-by-one, and at the end of each iteration that String is dropped. At the end of the loop, the Vec itself is dropped. However, v_tag might reference one of these String values, which means you could have a Version that borrows from a String that no longer exists. This is a "use after free" bug that Rust has discovered in your code.
To fix this, iterate over tags without consuming it. You can do this by iterating over a reference to the Vec:
for tag in &tags {
Or by using the iter() method:
for tag in tags.iter() {
In both of these cases, tag has type &String. In your code, tag has type String. This is an important distinction!
I am reading a book of the "The Rust Programming Language". Here are two example codes extracted from the book.
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // this empties the String, making it equal to ""
// println!("the first word is: {}", word);
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:18:5
|
16 | let word = first_word(&s);
| -- immutable borrow occurs here
17 |
18 | s.clear(); // error!
| ^^^^^^^^^ mutable borrow occurs here
19 |
20 | println!("the first word is: {}", word);
| ---- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` due to previous error
I know why the error exists and this happens when we use the second first_word function and then in the main function we clear the string and then use the result returned from the slice stored in word in the println macro. By then the slice does not exist anymore.
However, the error message emitted by the compiler is so confusing. I know we cannot have mutable and immutable of the same reference at the same time but we can have as many immutable references as we can. But I am just wondering why it says "cannot borrow s as mutable because it is also borrowed as immutable". Because we only have one immutable reference s within the main function. I know the clear function take a mut reference of self (&self). If that is the case, then it makes sense but then how come if we use the first first_word function, and then it compiles and nothing happens? I just don't get it.
Maybe you have a typo, because clear() does not take a &self but a &mut self, because it mutates the value it operates upon.
In the second version it is as if you wrote:
fn first_word<'a>(s: &'a String) -> &'a str { /*...*/ }
//...
let word: &str = first_word(&s);
In the declaration of word you cannot specify the lifetime of the reference, that in &'_ str, it is deduced automatically, by using the declaration of fist_word to be the same as the lifetime of the argument, that is the borrow of &s.
This means that s is borrowed for as long as word exists. That is, if you add a println!("{}", word) ad the end of main then s is borrowed so long, and the call to s.clear() fails, as String::clear(&mut self) requires a mutable borrow that cannot exist here.
If you remove the println!("{}", word), then the first borrow can be shortened and the program compiles again.
What about your first example? Well, there the function is as:
fn first_word<'a>(s: &'a String) -> usize { /*...*/ }
Note that the returned value does not have a lifetime, so it does not return a borrow. That is when you write:
let word = first_word(&s);
s is borrowed to call the function and when it returns it is unborrowed. Since there is no such living borrow you can call s.clear() without issue. Yes, now the word variable does not contain the actual length of s, and that may be wrong for you, but that is of no concern to the compiler because that does create unsafety (in the rusty sense of the word).
I have two versions of a function that are intended to do the same thing.
Version 1 - Works!
pub fn example1() {
// Make a mutable slice
let mut v = [0, 1, 2, 3];
// Make a mutable reference to said slice
let mut v_ref = &mut v[..];
let len = v_ref.len();
// Reduce slice to sub-slice -> np ;)
v_ref = {
// Create sub-slices
let (v_l, v_r) = {
// Involves some edits -> need mut
v_ref.swap(0, len - 1);
{ v_ref }.split_at_mut(len / 2)
};
// Choose slice with which to overwrite
// (involves some non-trivial condition here)
match len % 2 {
0 => v_l,
_ => v_r,
}
};
// And then we do something with v_ref
println!("{:?}", v_ref);
}
Essentially:
We start with a mutable variable, mut v_ref: &mut [i32], containing a mutable reference to a slice
We make two sub-slices from v_ref using split_at_mut*
The variable v_ref is overwritten to hold one of the sub-slices
*(Note - We avoid the problem of having two mutable references by moving v_ref, as opposed to reborrowing, by using an identity block)
(Regarding the intent of the code - this slice reduction is intended to be repeated in a loop; however this detail does not affect the problem)
Version 2 - Fails to compile
Version 2 is nearly identical to version 1, except that the sub-slice creation is moved to its own function:
fn example2_inner(v_ref: &mut [i32]) -> (&mut [i32], &mut [i32]) {
// Recreate len variable in function scope
let len = v_ref.len();
// Same mutation here
v_ref.swap(0, len - 1);
// Same slice split here
v_ref.split_at_mut(len / 2)
}
pub fn example2() {
let mut v = [0, 1, 2, 3];
let mut v_ref = &mut v[..];
let len = v_ref.len();
// This now fails to compile :(
v_ref = {
// Mutating & slice spliting moved to function
let (v_l, v_r) = example2_inner({ v_ref });
match len % 2 {
0 => v_l,
_ => v_r,
}
};
println!("{:?}", v_ref);
}
When I try to build this, I get the following errors:
error[E0506]: cannot assign to `v_ref` because it is borrowed
--> src/lib.rs:19:5
|
19 | / v_ref = {
20 | | // Mutating & slice spliting moved to function
21 | | let (v_l, v_r) = example2_inner({ v_ref });
| | ----- borrow of `v_ref` occurs here
22 | |
... |
26 | | }
27 | | };
| |_____^ assignment to borrowed `v_ref` occurs here
error[E0502]: cannot borrow `v_ref` as immutable because `*v_ref` is also borrowed as mutable
--> src/lib.rs:29:22
|
21 | let (v_l, v_r) = example2_inner({ v_ref });
| ----- mutable borrow occurs here
...
29 | println!("{:?}", v_ref);
| ^^^^^ immutable borrow occurs here
30 | }
| - mutable borrow ends here
Questions
Why don't these two examples compile the same way? Are mutable reference move semantics (i.e., the {vref} from E1) enforced for methods (i.e. split_at_mut), but not functions (i.e., example2_inner)? Why is this the case?
I would like to keep example2_inner as an independent utility function: how would I change Example 2 to accommodate that?
You can fix it like this:
v_ref = {
// move `v_ref` to a new variable which will go out of scope by the end of the block
let r = v_ref;
// Mutating & slice splitting moved to function
let (v_l, v_r) = example2_inner(r);
match len % 2 {
0 => v_l,
_ => v_r,
}
};
The reason is that v_1 and v_2 both are references to v_ref, but these references both outlive the block because they are being returned from it. Then you try to write to v_ref, which the borrow checker doesn't like because there are still these live references around.
Assigning v_ref to a new variable, r, means that v_ref is no longer valid - the variable has been moved. The live references, v_1 and v_2 are now referring to r, which in turn is a reference to v, and which only lives as long as the block. You are now free to write to v_ref because nothing else is referring to it.
Alternatively, you can just update to Rust Edition 2018, or enable non-lexical lifetimes, which can handle this situation.
Why don't these two examples compile the same way? Are mutable
reference move semantics (i.e., the {vref} from E1) enforced for
methods (i.e. split_at_mut), but not functions (i.e., example2_inner)?
Why is this the case?
Actually, it is not about methods vs functions, but about method call syntax vs function call syntax.
Every method call can be translated into the UFCS (Universal Function Call Syntax) which is generally of this form:
<Type as Trait>::method(args);
If we make a naive attempt to translate the call of { v_ref }.split_at_mut(len / 2) into UFCS in version 1, we end up with the same error as in version 2:
<[_]>::split_at_mut({v_ref}, len / 2)
The reason is that the above is equivalent to something which again does not cause v_ref to be moved into the block:
<[_]>::split_at_mut({&mut *v_ref}, len / 2)
What the method syntax actually resolves to is this working variant of the above:
<[_]>::split_at_mut(&mut *{v_ref}, len / 2)
For this variant, the compiler infers that v_ref indeed should be moved into the block, because the compiler realizes that the re-borrowing necessary for the method call is already going to happen on {v_ref}, so it doesn't insert an additional superfluous re-borrow on v_ref.
Now that we know how the method syntax solves your issue implicitly, we have an alternative solution for your issue in version 2:
example2_inner(&mut *{ v_ref });