Rust borrow checker throwing error in if statement - rust

I'm running through some LeetCode challenges to build up my understanding of Rust. I'm trying to write the following program that takes an i32 input, converts it to a String, reverses the digits, and returns back an i32 number.
In the case of negative numbers, e.g. -132, when the number is reversed the hyphen has to be popped off the stack: -132 -> 231- -> 231.
I've written the following code but I'm bumping up against the borrow checker, can anyone help?
impl Solution {
pub fn reverse(x: i32) -> i32 {
if(x == 0){
return x;
}
let reversed : std::iter::Rev<std::str::Chars> = x.to_string().chars().rev();
if reversed.last().unwrap() == '-' { //error occurs here
return reversed.collect::<String>()[0..reversed.count()].parse::<i32>().unwrap();
} else {
return reversed.collect::<String>().parse::<i32>().unwrap();
}
}
}
Line 6, Char 61: temporary value dropped while borrowed (solution.rs)
|
6 | let reversed : &std::iter::Rev<std::str::Chars> = &x.to_string().chars().rev();
| ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
7 | if reversed.last().unwrap() == '-' {
| -------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
Line 7, Char 12: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
7 | if reversed.last().unwrap() == '-' {
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Line 8, Char 20: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
8 | return reversed.collect::<String>()[0..reversed.count()].parse::<i32>().unwrap();
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Line 8, Char 52: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
8 | return reversed.collect::<String>()[0..reversed.count()].parse::<i32>().unwrap();
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Line 10, Char 20: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
10 | return reversed.collect::<String>().parse::<i32>().unwrap();
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Here's the error reproduced in a playground

Original error reproduced in this playground
Here's a solution that is close to your approach that fixes the error:
fn reverse(x: i32) -> i32 {
if x == 0 {
return x;
}
let mut reversed:String = x.to_string().chars().rev().collect::<String>();
if reversed.chars().last() == Some('-') {
reversed.pop();
}
reversed.parse::<i32>().unwrap()
}
working version: playground
This other post has good explanation of why. In the context of this question:
x.to_string().chars().rev();
// ^ ^
// String <- &str
to_string returns a String, but the code has no reference to that String after this statement, so it needs to free the String, but the iterator refers to the &str from chars() which then becomes a reference to something that no longer exists. By changing the type of reversed to String and using collect then Rust can bind the new data to the local variable and doesn't have to drop it at the end of the statement.

Does the LeetCode challenge impose the int→string→int conversions? I would do it directly on ints:
fn reverse (x: i32) -> i32 {
let mut x = x.abs();
let mut y = 0;
while x != 0 {
y = y*10 + x%10;
x = x/10;
}
return y;
}

Why not just take the absolute value of x before converting it to a String so you don't have to deal with the hyphen edge case?
fn reverse(x: i32) -> i32 {
x.abs()
.to_string()
.chars()
.rev()
.collect::<String>()
.parse::<i32>()
.unwrap()
}
fn main() {
assert_eq!(reverse(1234567), 7654321);
assert_eq!(reverse(-1234567), 7654321);
}
playground
Even if we get the input as a String and have to deal with the hyphen the most idiomatic solution would be to filter() it out:
fn reverse(x: String) -> i32 {
x.chars()
.filter(|&c| c != '-')
.rev()
.collect::<String>()
.parse::<i32>()
.unwrap()
}
fn main() {
assert_eq!(reverse(1234567.to_string()), 7654321);
assert_eq!(reverse((-1234567).to_string()), 7654321);
}
playground

Related

Rust String::to_bytes | What does the Rust compiler mean here, exactly?

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")

Why does using a mutable and an immutable reference in the same expression involving the += operator seem to be sometimes allowed, sometimes not?

In the below code example, I'm trying to increment the member variable a of the struct X via a mutable reference to it, in four different ways. Here, the compiler gives the following error for the line denoted by B:
error[E0502]: cannot borrow `*x` as immutable because it is also borrowed as mutable
--> src\main.rs:17:23
|
17 | *x.get_a_mut() += x.get_a(); //B DOESN'T COMPILE
| ------------------^--------
| || |
| || immutable borrow occurs here
| |mutable borrow occurs here
| mutable borrow later used here
If it is a problem to use a mutable and an immutable reference to a in the same expression, why does C and D compile?
struct X {
a: i64,
}
impl X {
pub fn get_a_mut(&mut self) -> &mut i64 {
return &mut self.a;
}
pub fn get_a(&self) -> &i64 {
return &self.a;
}
}
fn my_fn(x: &mut X) {
*x.get_a_mut() += 5; //A
*x.get_a_mut() += x.get_a(); //B DOESN'T COMPILE
*x.get_a_mut() += 2 * x.get_a(); //C
*x.get_a_mut() = x.get_a() + x.get_a(); //D
}
fn main() {
let mut x = X { a: 50 };
my_fn(&mut x);
}
According to += documentation, you are calling something like add_assign(lhs: &mut i64, rhs: &i64) in case B and something like add_assign(lhs: &mut i64, rhs: i64) in cases A, C and D.
In case A, rhs is a constant, different from x.a; no problem.
In case C, rhs is a temporary (the result of 2 * x.get_a()) and does not need to keep a reference on x.a to exist; no problem.
In case D, rhs is a temporary (the result of x.get_a() + x.get_a()) and does not need to keep a reference on x.a to exist; no problem.
But when it comes to case B, rhs is a reference on x.a; then this call uses both a mutable (lhs) and immutable (rhs) reference on the same data (x.a) at the same time, which is forbidden.
You could eventually clone rhs: *x.get_a_mut() += x.get_a().clone().

How do I parse a vector into a function?

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]) {

Variable lifetime qs in function [duplicate]

This question already has answers here:
Return local String as a slice (&str)
(7 answers)
Proper way to return a new string in Rust
(2 answers)
Why can I return a reference to a local literal but not a variable?
(1 answer)
Closed 4 years ago.
fn get_str1<'a>() -> &'a str {
let x = "hello";
return x;
}
fn get_str2<'a>(str1: &str) -> &'a str {
let x: &'a str = (str1.to_string() + "123").as_str();
return x;
}
fn get_str3<'a>(str1: &str) -> &'a str {
let tmp = str1.to_string() + "123";
let x: &'a str = tmp.as_str();
return x;
}
#[test]
fn lifetime_test() {
println!("{}", get_str1());
println!("{}", get_str2("hello"));
println!("{}", get_str3("hello"))
}
When I call get_str1, it has no problem, but when I call get_str2, it has a compilation error:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:7:22
|
7 | let x: &'a str = (str1.to_string() + "123").as_str();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 6:1...
--> src/main.rs:6:1
|
6 | fn get_str2<'a>(str1: &str) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: consider using a `let` binding to increase its lifetime
When I call get_str3, it also has a compilation error:
error[E0597]: `tmp` does not live long enough
--> src/main.rs:13:22
|
13 | let x: &'a str = tmp.as_str();
| ^^^ borrowed value does not live long enough
14 | return x;
15 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 11:1...
--> src/main.rs:11:1
|
11 | fn get_str3<'a>(str1: &str) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Why do I get these errors and how do I fix get_str2 and get_str3?
The first function works because the string has the 'static lifetime and will be promoted by the compiler.
As for the others..
fn get_str2<'a>(str1: &str) -> &'a str {
let x = (str1.to_string() + "123").as_str();
return x
}
This part: str1.to_string(), isn't returning a string slice... but a new instance of a String. This is basically the same as this:
let x = str1.to_string(); // this is of type String, and its lifetime is local to this function
let y = str1 + "123"; // This consumes str1 and appends a &str to it - thus the lifetime is still of the new String instance above
let z = y.as_str(); // This returns a reference to the local String instance
Reading each comment above, it becomes clear that you're actually trying to return a reference to a local String. You can't do this, because the String will be destroyed at the end of the function and the reference will be invalid.
This applies to your third function as well. You're returning a reference to a String instance that will be destroyed at the end of the function.

Borrowed value does not live long enough when creating a Vec

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.

Resources