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")
Related
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'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)
I am really new to Rust, I am having trouble solving this error, but it only happens if I comment out the while statement , basicly I am asking values from the console and storing it in a HashMap:
use std::collections::HashMap;
use std::io;
fn main() {
let mut customers = HashMap::new();
let mut next_customer = true;
while next_customer {
let mut input_string = String::new();
let mut temp_vec = Vec::with_capacity(3);
let mut vec = Vec::with_capacity(2);
println!("Insert new customer f.e = customer id,name,address:");
io::stdin().read_line(&mut input_string);
input_string = input_string.trim().to_string();
for s in input_string.split(",") {
temp_vec.push(s);
}
vec.push(temp_vec[1]);
vec.push(temp_vec[2]);
let mut key_value = temp_vec[0].parse::<i32>().unwrap();
customers.insert(key_value, vec);
next_customer = false;
}
println!("DONE");
}
The code results in the error
error[E0597]: `input_string` does not live long enough
--> src/main.rs:14:18
|
14 | for s in input_string.split(",") {
| ^^^^^^^^^^^^ borrowed value does not live long enough
...
20 | customers.insert(key_value, vec);
| --------- borrow later used here
21 | next_customer = false;
22 | }
| - `input_string` dropped here while still borrowed
As others have said the problem lies with the lifetime and/or type of the values getting put into the customers map.
customers.insert(key_value, vec);
| --------- borrow later used here
Often this happens when the compiler has decided to give an object a type that you didn't expect. To find out what it's doing you can force the type, and see how it complains. Changing the code to:
let mut customers: HashMap<(),()> = HashMap::new();
Gives us two relevant errors:
20 | customers.insert(key_value, vec);
| ^^^^^^^^^ expected `()`, found `i32`
...
20 | customers.insert(key_value, vec);
| ^^^ expected `()`, found struct `std::vec::Vec`
|
= note: expected unit type `()`
found struct `std::vec::Vec<&str>`
So the type that the compiler wants to give our customers object is HashMap<i32, Vec<&str>>
The problem is that the &str lifetime has got to be inside the block as we don't store the Strings anywhere, and they can't have 'static lifetime since they're user input.
This means we probably want a HashMap<i32,Vec<String>>.
Changing the code to use one of those gives us an error about vec not having the right type: It's getting deduced as a Vec<&str>, but we want a Vec<String>.
We have two options.
Convert the vec to the right type just before we insert it into the map using customers.insert(key_value, vec.iter().map(|s| s.to_string()).collect()). (Though you may want to extract it to a variable for clarity).
Explicitly change the type of vec to Vec<String>
Option 1 "just works". While option 2 leads us down a path of making similar changes closer and closer to the read_line call.
Once you've decided on the fix in option 1, you can remove the manual type annotations that were added to work out the fix, if you find them overly noisy.
The issue is that you are passing around reference to underlying &str values that will get dropped. One way is to take the input string, trim and split it, then clone it going into the other vector.
let temp_vec: Vec<String> = input_string.trim().split(",").map(|t| t.to_string()).collect();
vec.push(temp_vec[1].clone());
vec.push(temp_vec[2].clone());
The idea is to send a set of characters of a vector and let the function display the current correct guesses.
Here is my main:
fn main() {
let mut guessedLetters = vec![];
displayWord(guessedLetters);
}
And here is the function:
fn displayWord(correctGuess: Vec<char>) {
let mut currentWord = String::new();
for x in 0..5 {
currentWord.push(correctGuess[x]);
}
println!("Current guesses: {}", currentWord);
}
I don't know what I'm supposed to write inside the parameters of displayWord.
There's a couple of things wrong with your code.
The first error is pretty straight forward:
--> src/main.rs:38:25
|
38 | displayWord(guessed_Letters);
| ^^^^^^^^^^^^^^^ expected char, found enum `std::option::Option`
|
= note: expected type `std::vec::Vec<char>`
found type `std::vec::Vec<std::option::Option<char>>`
The function you wrote is expecting a vector a characters ... but you're passing it a vector of Option<char>. This is happening here:
guessed_Letters.push(line.chars().nth(0));
According to the documentation, the nth method returns an Option. The quick fix here is to unwrap the Option to get the underlying value:
guessed_Letters.push(line.chars().nth(0).unwrap());
Your next error is:
error[E0382]: use of moved value: `guessed_Letters`
--> src/main.rs:38:25
|
38 | displayWord(guessed_Letters);
| ^^^^^^^^^^^^^^^ value moved here in previous iteration of loop
|
= note: move occurs because `guessed_Letters` has type `std::vec::Vec<char>`, which does not implement the `Copy` trait
This is transferring ownership of the vector on the first iteration of the loop and the compiler is telling you that subsequent iterations would be in violation of Rust's ownership rules.
The solution here is to pass the vector by reference instead:
displayWord(&guessed_Letters);
..and your method should also accept a reference:
fn displayWord(correctGuess: &Vec<char>) {
let mut currentWord = String::new();
for x in 0..5 {
currentWord.push(correctGuess[x]);
}
println!("Current guesses: {}", currentWord);
}
This can be shortened to use a slice and still work:
fn displayWord(correctGuess: &[char]) {
Editor's note: This question was asked before Rust 1.0. Since then, many functions and types have changed, as have certain language semantics. The code in the question is no longer valid, but the ideas expressed in the answers may be.
I'm trying to list the files in a directory and copy the filename to my own Vec. I've tried several solutions, but it always ends up with a problem of not being able to create long enough living variables. I don't understand my mistake.
fn getList(action_dir_path : &str) -> Vec<&str> {
let v = fs::readdir(&Path::new(action_dir_path))
.unwrap()
.iter()
.map(|&x| x.filestem_str().unwrap())
.collect();
return v;
}
Why does the compiler complain about "x" ? I don't care about x, I want the &str inside it and I thought &str were static.
I tried this way, but I got the same result with the compiler complaining about "paths" not living long enough.
fn getList2(action_dir_path : &str) -> Vec<&str> {
let paths = fs::readdir(&Path::new(action_dir_path)).unwrap();
let mut v : Vec<&str> = Vec::new();
for path in paths.iter(){
let aSlice = path.filestem_str().unwrap();
v.push(aSlice);
}
return v;
}
Here is the playground.
The most literal translation of your code that supports Rust 1.0 is this:
use std::{fs, path::Path, ffi::OsStr};
fn getList(action_dir_path: &str) -> Vec<&OsStr> {
let v = fs::read_dir(&Path::new(action_dir_path))
.unwrap()
.map(|x| x.unwrap().path().file_stem().unwrap())
.collect();
return v;
}
This produces the error messages:
Rust 2015
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:6:18
|
6 | .map(|x| x.unwrap().path().file_stem().unwrap())
| ^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 3:1...
--> src/lib.rs:3:1
|
3 | / fn getList(action_dir_path: &str) -> Vec<&OsStr> {
4 | | let v = fs::read_dir(&Path::new(action_dir_path))
5 | | .unwrap()
6 | | .map(|x| x.unwrap().path().file_stem().unwrap())
7 | | .collect();
8 | | return v;
9 | | }
| |_^
Rust 2018
error[E0515]: cannot return value referencing temporary value
--> src/lib.rs:6:18
|
6 | .map(|x| x.unwrap().path().file_stem().unwrap())
| -----------------^^^^^^^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| temporary value created here
The problem comes from Path::file_stem. This is the signature:
pub fn file_stem(&self) -> Option<&OsStr>
This indicates that the method will return a borrowed reference to a OsStr. The PathBuf struct is the owner of the string. When you leave the method, there's nowhere left that owns the PathBuf, so it will be dropped. This means that any references into the PathBuf will no longer be valid. This is Rust preventing you from having references to memory that is no longer allocated, yay for Rust!
The easiest thing you can do is return a Vec<String>. String owns the string inside of it, so we don't need to worry about it being freed when we leave the function:
fn get_list(action_dir_path: &str) -> Vec<String> {
fs::read_dir(action_dir_path)
.unwrap()
.map(|x| {
x.unwrap()
.path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
})
.collect()
}
I also updated the style (at no charge!) to be more Rust-like:
Use snake_case for items
No space before the colon in type definitions
There's no reason to set a variable just to return it.
Don't use explicit return statements unless you are exiting from a function early.
There's no need to wrap the path in a Path.
However, I'm not a fan of all of the unwrapping. I'd write the function like this:
use std::{ffi::OsString, fs, io, path::Path};
fn get_list(action_dir_path: impl AsRef<Path>) -> io::Result<Vec<OsString>> {
fs::read_dir(action_dir_path)?
.map(|entry| entry.map(|e| e.file_name()))
.collect()
}
fn main() {
println!("{:?}", get_list("/etc"));
}
In addition to the changes above:
I use a generic type for the input path.
I return a Result to propagate errors to the caller.
I directly ask the DirEntry for the filename.
I leave the type as an OsString.
One small related point:
I thought &str were static.
&'static strs are static, but that's only one kind of &str. It can have any kind of lifetime.