Resolving borrowed value issue - rust

I have the following simple code. I am not able to figure out how to resolve the error "borrowed value does not live long enough".
fn main() {
let mut saved_message: Vec<&str> = Vec::new();
for i in 0..10 {
let mut tmp: String = String::from("line_num ");
tmp.push_str(&i.to_string());
saved_message.push(&tmp);
}
}
Compiling rust_test v0.1.0 (C:\workspace\rust_test)
error[E0597]: `tmp` does not live long enough
--> src\lib.rs:6:28
|
6 | saved_message.push(&tmp);
| -------------------^^^^-
| | |
| | borrowed value does not live long enough
| borrow later used here
7 | }
| - `tmp` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `rust_test` due to previous error

Error comes from this line:
let mut saved_message: Vec<&str> = Vec::new();
Here, you defined a vector of references to string slices.
Since you are storing references instead of the whole string, every reference that is stored in the vector, should outlive the vector itself. But this is not the case with your code as each tmp value in your for loop gets droped at the end of the loop (reference is no longer valid) and thus, pushing it to a vector that would outlive the for loop is not safe.
workarounds:
Create a vector of String instead of &str
fn main() {
let mut saved_message: Vec<String> = Vec::new();
for i in 0..10 {
let mut tmp: String = String::from("line_num ");
tmp.push_str(&i.to_string());
saved_message.push(tmp);
}
}
Notice the vector type hint which is now Vec<String> instead of Vec<&str>.
Note that now you should push tmp instead of &tmp in the last line.
You can also trust the compiler to guess the type for you and remove the whole type hint:
fn main() {
let mut saved_message = Vec::new();
for i in 0..10 {
let mut tmp = String::from("line_num ");
tmp.push_str(&i.to_string());
saved_message.push(tmp);
}
}

Related

Use of moved value `inb`

I'm trying to write a function, and I'm getting an error message about borrowing that I can't solve because I'm struggling to wrap my head around other responses on this site. Here's the function I've got so far:
use orgize::Org;
fn convert(i: String) -> String {
let inb: Vec<u8> = Vec::new();
Org::parse_string(i).write_html(inb);
let out = String::from_utf8(inb).unwrap();
return out;
}
And it's outputting the following error:
error[E0382]: use of moved value: `inb`
--> src/main.rs:9:30
|
7 | let inb: Vec<u8> = Vec::new();
| --- move occurs because `inb` has type `Vec<u8>`, which does not implement the `Copy` trait
8 | Org::parse_string(i).write_html(inb);
| --- value moved here
9 | let out = String::from_utf8(inb).unwrap();
| ^^^ value used here after move
When you call write_html(inb), you are moving inb. It cannot be used anymore.
Since write_html() accepts a generic W: Write, and Vec<u8> implements Write but also any &mut Write implements Write, you can just pass &mut inb to borrow inb instead of moving it:
fn convert(i: String) -> String {
let mut inb: Vec<u8> = Vec::new();
Org::parse_string(i).write_html(&mut inb);
let out = String::from_utf8(inb).unwrap();
return out;
}

Pulling a vec out of a vec of structs

I'm working on a compiler for a toy language and I want to be able to check for errors on each file. I have a MooFile struct that has a Vec<anyhow::Error> where I put errors when they are encountered. Now I want to go through the files, get the errors, and flatten that into a single vec to return from the compiler but I'm running up against the borrow checker. I'm going to post my various attempt but they keep running into similar issues.
fn compile(&mut self) -> Result<(), Vec<Error>> {
...
// Check for errors
let has_errors = self.files
.iter()
.map(|file| file.has_errors())
.reduce(|acc, value| acc | value)
.unwrap_or(false);
if has_errors {
let mut errors = vec![];
self.files
.iter()
.for_each(|&mut file| errors.append(&mut file.errors));
Err(errors)
} else {
Ok(())
}
}
error[E0596]: cannot borrow `file.errors` as mutable, as it is behind a `&` reference
--> src\lib.rs:62:48
|
62 | .for_each(|file| errors.append(&mut file.errors));
| ---- ^^^^^^^^^^^^^^^^ `file` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| help: consider changing this to be a mutable reference: `&mut MooFile`
fn compile(&mut self) -> Result<(), Vec<Error>> {
...
// Check for errors
let has_errors = self.files
.iter()
.map(|file| file.has_errors())
.reduce(|acc, value| acc | value)
.unwrap_or(false);
if has_errors {
let mut errors = vec![];
self.files
.into_iter()
.for_each(|mut file| errors.append(&mut file.errors));
Err(errors)
} else {
Ok(())
}
}
error[E0507]: cannot move out of `self.files` which is behind a mutable reference
--> src\lib.rs:63:13
|
63 | self.files
| ^^^^^^^^^^ move occurs because `self.files` has type `Vec<MooFile>`, which does not implement the `Copy` trait
64 | .into_iter()
| ----------- `self.files` moved due to this method call
fn compile(&mut self) -> Result<(), Vec<Error>> {
...
// Check for errors
let errors: Vec<Error> = self.files
.iter()
.map(|file| file.errors)
.flatten()
.collect();
if errors.len() > 0 {
Err(errors)
} else {
Ok(())
}
}
error[E0507]: cannot move out of `file.errors` which is behind a shared reference
--> src\lib.rs:54:25
|
54 | .map(|file| file.errors)
| ^^^^^^^^^^^ move occurs because `file.errors` has type `Vec<anyhow::Error>`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `moo2` due to previous error
I feel like there has to be some kind of idiomatic way of doing this kind of thing that I'm missing.
Most of these errors come from how you create your iterators. If you assume x is a Vec<Y>:
x.iter(): Consumes &x and creates an iterator of &Y.
x.iter_mut(): Consumes &mut x and creates an iterator of &mut Y.
x.into_iter(): Consumes ownership of the entire Vec<Y> and creates an iterator of owned Y.
The first version uses .iter() when you want a mutable reference (.iter_mut()). The second uses .into_iter() when you want a mutable reference (.iter_mut()). And the third uses .iter() when you attempt to claim ownership of the errors by disposing of the files (.into_iter(), but you can't drop self.files without issues).
Here is one way you could Clone the errors without moving them out of each file.
let mut errors = Vec::new();
self.files.iter()
.filter(|file| file.has_errors())
.foreach(|file| errors.extend_from_slice(&file.errors));
if has_errors {
return Err(errors)
}
Ok(())
Or you could drain them into a new Vec using extend. This version also uses any to look a bit cleaner, but is not as efficient.
if self.files.iter().any(|f| f.has_errors()) {
let mut errors = Vec::new();
for file in &mut self.files {
errors.extend(file.errors.drain(..));
}
return Err(errors)
}
Ok(())

What's the proper way to use variables defined in the main thread in a child thread in Rust?

I'm new to Rust and still reading the Rust book. Below is my program.
use clap::{App, Arg};
type GenericError = Box<dyn std::error::Error + Send + Sync + 'static>;
type GenericResult<T> = Result<T, GenericError>;
fn main() -> GenericResult<()> {
let matches = App::new("test")
.arg(Arg::new("latency")
.takes_value(true))
.get_matches();
let latency_present = matches.is_present("latency");
let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
let latency_pairs: Vec<&str> = latency.split(",").collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
checker.join().unwrap()?;
Ok(())
}
When I run it, it tells me this:
error[E0597]: `matches` does not live long enough
--> src\main.rs:14:19
|
14 | let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
| ^^^^^^^--------------------
| |
| borrowed value does not live long enough
| argument requires that `matches` is borrowed for `'static`
...
30 | }
| - `matches` dropped here while still borrowed
I don't quite understand the error messages here. But I guess it's because I use latency_pairs in the checker thread and latency_pairs could get dropped while checker is still executing. Is my understanding correct? How to fix the error? I tried for (i, latency_pair) in latency_pairs.clone().iter().enumerate() { in order to pass a cloned value for the thread, but it doesn't help.
latency_pairs holds references into latency which in turn references matches. Thus cloning latency_pairs just clones the references into latency and matches.
Your code would require that latency's type is &'static str but it's actually &'a str where 'a is bound to matches' lifetime.
You can call to_owned() on latency to get an owned value and split the string inside the closure or you can call to_owned() on each of the splits collected in latency_pairs and move that Vec<String> into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
If you need to use latency_pairs outside of the closure, you can clone it before moving it into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let latency_pairs_ = latency_pairs.clone();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs_.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
println!("{:?}", latency_pairs);

Iterate through a whole file one character at a time

I'm new to Rust and I'm struggle with the concept of lifetimes. I want to make a struct that iterates through a file a character at a time, but I'm running into issues where I need lifetimes. I've tried to add them where I thought they should be but the compiler isn't happy. Here's my code:
struct Advancer<'a> {
line_iter: Lines<BufReader<File>>,
char_iter: Chars<'a>,
current: Option<char>,
peek: Option<char>,
}
impl<'a> Advancer<'a> {
pub fn new(file: BufReader<File>) -> Result<Self, Error> {
let mut line_iter = file.lines();
if let Some(Ok(line)) = line_iter.next() {
let char_iter = line.chars();
let mut advancer = Advancer {
line_iter,
char_iter,
current: None,
peek: None,
};
// Prime the pump. Populate peek so the next call to advance returns the first char
let _ = advancer.next();
Ok(advancer)
} else {
Err(anyhow!("Failed reading an empty file."))
}
}
pub fn next(&mut self) -> Option<char> {
self.current = self.peek;
if let Some(char) = self.char_iter.next() {
self.peek = Some(char);
} else {
if let Some(Ok(line)) = self.line_iter.next() {
self.char_iter = line.chars();
self.peek = Some('\n');
} else {
self.peek = None;
}
}
self.current
}
pub fn current(&self) -> Option<char> {
self.current
}
pub fn peek(&self) -> Option<char> {
self.peek
}
}
fn main() -> Result<(), Error> {
let file = File::open("input_file.txt")?;
let file_buf = BufReader::new(file);
let mut advancer = Advancer::new(file_buf)?;
while let Some(char) = advancer.next() {
print!("{}", char);
}
Ok(())
}
And here's what the compiler is telling me:
error[E0515]: cannot return value referencing local variable `line`
--> src/main.rs:37:13
|
25 | let char_iter = line.chars();
| ---- `line` is borrowed here
...
37 | Ok(advancer)
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0597]: `line` does not live long enough
--> src/main.rs:49:34
|
21 | impl<'a> Advancer<'a> {
| -- lifetime `'a` defined here
...
49 | self.char_iter = line.chars();
| -----------------^^^^--------
| | |
| | borrowed value does not live long enough
| assignment requires that `line` is borrowed for `'a`
50 | self.peek = Some('\n');
51 | } else {
| - `line` dropped here while still borrowed
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0515, E0597.
For more information about an error, try `rustc --explain E0515`.
error: could not compile `advancer`.
Some notes:
The Chars iterator borrows from the String it was created from. So you can't drop the String while the iterator is alive. But that's what happens in your new() method, the line variable owning the String disappears while the iterator referencing it is stored in the struct.
You could also try storing the current line in the struct, then it would live long enough, but that's not an option – a struct cannot hold a reference to itself.
Can you make a char iterator on a String that doesn't store a reference into the String? Yes, probably, for instance by storing the current position in the string as an integer – it shouldn't be the index of the char, because chars can be more than one byte long, so you'd need to deal with the underlying bytes yourself (using e.g. is_char_boundary() to take the next bunch of bytes starting from your current index that form a char).
Is there an easier way? Yes, if performance is not of highest importance, one solution is to make use of Vec's IntoIterator instance (which uses unsafe magic to create an object that hands out parts of itself) :
let char_iter = file_buf.lines().flat_map(|line_res| {
let line = line_res.unwrap_or(String::new());
line.chars().collect::<Vec<_>>()
});
Note that just returning line.chars() would have the same problem as the first point.
You might think that String should have a similar IntoIterator instance, and I wouldn't disagree.

error[E0502]: cannot borrow `char_array` as mutable because it is also borrowed as immutable

I am writing a small program to identify the first recurring character in a string:
use std::io;
fn main() {
let mut main_string = String::new();
println!("Please enter the string : ");
io::stdin()
.read_line(&mut main_string)
.expect("Failed to read the input value");
main_string = main_string.trim().to_string();
println!("The trimmed string is : {}", main_string);
let repeating_character = recurring_string_parser(main_string);
println!(
"The character which is first repeating is {}",
repeating_character
);
}
fn recurring_string_parser(main_string: String) -> char {
let mut char_array = Vec::new();
for each_char in main_string.chars() {
let mut some_val = char_array.iter().find(|&&c| c == each_char);
match some_val {
Some(ch) => return each_char,
_ => println!("do nothing"),
}
char_array.push(each_char);
println!(" The charcater is {:?}", some_val);
}
return 'a';
}
Error:
error[E0502]: cannot borrow `char_array` as mutable because it is also borrowed as immutable
--> src/main.rs:31:9
|
25 | let mut some_val = char_array.iter().find(|&&c| c == each_char);
| ---------- immutable borrow occurs here
...
31 | char_array.push(each_char);
| ^^^^^^^^^^ mutable borrow occurs here
32 | println!(" The charcater is {:?}", some_val);
33 | }
| - immutable borrow ends here
What is it that I am doing wrong? It would be of great help if someone could explain it as well because I am finding it difficult to grasp the concept of mutable borrowing.
The return type of char_array.iter().find(|&&c| c == each_char) is an Option<&char>; a reference pointing at the original item in char_array. You then attempt to modify char_array which could invalidate any references. After that, you try to access the now-potentially-invalid value by printing it.
In a language like C or C++, this code would be allowed but would occasionally crash, corrupt data, allow for arbitrary code execution, or eat your laundry.
The laziest solution is to reorder the println and add an extra set of braces around where some_val should live. (In the future we will have Non-Lexical Lifetimes and the extra braces won't be needed):
{
let mut some_val = char_array.iter().find(|&&c| c == each_char);
match some_val {
Some(ch) => return each_char,
_ => println!("do nothing"),
}
println!(" The character is {:?}", some_val);
}
char_array.push(each_char);
You could also clone the value, breaking the relation between some_val and char_array:
let mut some_val = char_array.iter().find(|&&c| c == each_char).cloned();
See also:
Unable to borrow an iterator as mutable more than once at a time
Why does iter() borrow the list as immutable?

Resources