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).
Related
fn first_word(s: &str) -> &str {
"there"
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear();
println!("the first word is: {}", word);
}
The compiler complains:
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:8:5
|
7 | let word = first_word(&s);
| -- immutable borrow occurs here
8 | s.clear();
| ^^^^^^^^^ mutable borrow occurs here
9 | println!("the first word is: {}", word);
| ---- immutable borrow later used here
I'm learning rust and this is a demo from the rust book which I changed a little bit. https://rust-book.cs.brown.edu/ch04-03-slices.html
I'm confused about the error message at the bottom line where it says word immutably borrows from s while logically this hasn't to be the case. Does the compiler always assume that the returned slice must point to the sole argument somehow? Does this have anything to do with lifetime? What is the general reasoning rule here?
You're returning a string literal, &'static str. But the compiler never looks at function bodies while type checking - it only looks at their signatures.
first_word()'s signature is fn first_word(s: &str) -> &str, which by the lifetime elision rules is the same as fn first_word<'a>(s: &'a str) -> &'a str. So the compiler assumes the returned string borrows from the parameter string. It works because &'static str, which is actually returned, can be shortened to any other lifetime.
If you will declare first_word() as fn first_word(s: &str) -> &'static str then it'll work.
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")
I'm new to Rust and looks like I'm seriously missing some concept here.
use std::thread;
fn main() {
let mut children = vec![];
//spawn threads
for i in 0..10 {
let c = thread::spawn(|| {
println!("thread id is {}", i);
});
children.push(c);
}
for j in children {
j.join().expect("thread joining issue");
}
}
It fails with the error:
error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function
Since the type of i is i32 , and there are no references involved shouldn't Rust copy the value instead of forcing to move ?
The answer to your original question is that println! borrows its arguments. However, as you pointed out in the comments, even (apparently) moving the integer into the closure still causes a compile error.
For the purposes of this answer, we'll work with this code.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(|| {
let _y = x;
});
}
(playground)
use_closure simulates what thread::spawn does in the original code: it consumes a closure whose type has to be 'static.
Attempting to compile this gives the error
error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
--> src/main.rs:5:17
|
5 | use_closure(|| {
| ^^ may outlive borrowed value `x`
6 | let _y = x;
| - `x` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:5:5
|
5 | / use_closure(|| {
6 | | let _y = x;
7 | | });
| |______^
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
5 | use_closure(move || {
| ^^^^^^^
Wait, what?
6 | let _y = x;
| - `x` is borrowed here
Why is x borrowed there? Shouldn't it be a copy? The answer lies in "capture modes" for closures. From the documentation
The compiler prefers to capture a closed-over variable by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally by move. It will pick the first choice of these that allows the closure to compile. The choice is made only with regards to the contents of the closure expression; the compiler does not take into account surrounding code, such as the lifetimes of involved variables.
Precisely because x has a Copy type, the closure itself can compile with a mere immutable borrow. Given an immutable borrow of x (call it bor), we can do our assignment to _y with _y = *bor. This isn't a "move out of data behind a reference" because this is a copy instead of a move.
However, since the closure borrows a local variable, its type won't be 'static, so it won't be usable in use_closure or thread::spawn.
Trying the same code with a type that isn't Copy, it actually works perfectly, since the closure is forced to capture x by moving it.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: Vec<i32> = vec![];
use_closure(|| {
let _y = x;
});
}
(playground)
Of course, as you already know, the solution is to use the move keyword in front of the closure. This forces all captured variables to be moved into the closure. Since the variable won't be borrowed, the closure will have a static type and will be able to be used in use_closure or thread::spawn.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(move || {
let _y = x;
});
}
(playground)
This question already has an answer here:
What is lifetime elision in very simple terms?
(1 answer)
Closed 3 years ago.
I'm new to Rust and I'm reading the The Rust Programming Language online book. I'm now stuck on a problem about the Rust's borrow check, the code example is shown below:
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if b == b' ' {
return &s[..i];
}
}
&s[..]
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear();
println!("word = {}", word);
}
Rust compiler complains about the code with the following error message:
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:16:5
|
15 | let word = first_word(&s);
| -- immutable borrow occurs here
16 | s.clear();
| ^^^^^^^^^ mutable borrow occurs here
17 |
18 | println!("word = {}", word);
| ---- immutable borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
error: Could not compile `demo`.
To learn more, run the command again with --verbose.
The online book explains that when executing s.clear(), a new mutable reference to s is created and conflicted with the existing immutable reference word because word is not out of its scope until the last println! statement. It seems like that somehow, Rust's borrow checker figured out that word, which is returned by the first_word function, refers to s. How does it achieve that?
When you have
fn first_word(s: &String) -> &str {
it really means
fn first_word<'a>(s: &'a String) -> &'a str {
(see lifetime elision).
That is, the lifetime of the output is the one of the input.
That's how the borrow checker can deduce that word, which is returned by the first_word function, refers to s.
In section 3.2 of the Nomicon, under the heading "liveness", it says
However it's often the case that Rust isn't sufficiently smart to
prove that multiple borrows are disjoint.
What is an example where the Rust compiler cannot prove that they are disjoint? Will this ever occur in a tuple struct?
The key is in the previous sentence:
Rust explicitly enables [reborrowing into multiple mutable references] to be done with disjoint struct fields, because disjointness can be statically proven
Outside of this case, the compiler cannot tell that two borrows are disjoint. In practice, this means that the compiler cannot tell that borrows resulting from a function call will be disjoint.
struct Thing {
a: i32,
b: i32,
}
fn example_works(thing: &mut Thing) {
let a = &mut thing.a;
let b = &mut thing.b;
}
fn get_a(thing: &mut Thing) -> &mut i32 {
&mut thing.a
}
fn get_b(thing: &mut Thing) -> &mut i32 {
&mut thing.b
}
fn example_doesnt_work(thing: &mut Thing) {
let a = get_a(thing);
let b = get_b(thing);
println!("{}, {}", a, b);
}
error[E0499]: cannot borrow `*thing` as mutable more than once at a time
--> src/lib.rs:26:19
|
25 | let a = get_a(thing);
| ----- first mutable borrow occurs here
26 | let b = get_b(thing); // cannot borrow `*thing` as mutable more than once at a time
| ^^^^^ second mutable borrow occurs here
27 | println!("{}, {}", a, b);
| - first borrow later used here
Will this ever occur in a tuple struct?
Not specifically because it's a tuple struct, but yes, it can happen for the same reasons. If you obtain a borrow from a function call, you will get the same problem as a "traditional" struct.