Adrian Coutsoftides
Adrian Coutsoftides

Reputation: 1293

Rust borrow checker throwing error in if statement

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

Upvotes: 0

Views: 204

Answers (3)

Ultrasaurus
Ultrasaurus

Reputation: 3169

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.

Upvotes: 3

Jmb
Jmb

Reputation: 23463

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;
}

Upvotes: 2

pretzelhammer
pretzelhammer

Reputation: 15165

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

Upvotes: 1

Related Questions