0

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

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Adrian Coutsoftides
  • 1,203
  • 1
  • 16
  • 38
  • 2
    Please include the error message in the question. Also, please indicate whether you read the whole error (not just the part on the marked line). If so, did you try to understand and implement the compiler's suggestion? – user4815162342 Feb 02 '21 at 22:10
  • @user4815162342 yes definetly read the whole error a few times, I went back and forth trying to make `reversed` a reference to `x` instead of the value directly, but would keep bumping into all sorts of errors. I have updated the question with the error. – Adrian Coutsoftides Feb 02 '21 at 22:13
  • 3
    Ok, so the compiler is telling you that the "value doesn't live long enough" with the hint "consider using a `let` binding to create a longer lived value". It shows that `x.to_string()` creates the temporary value that doesn't live long enough. You can start with `let s = x.to_string()` (the `let` binding the compiler refers to) and then use `s` instead. That allows the `reversed` iterator to refer to chars owned by a value that actually exists (because it's in a `let` binding guaranteed to exist until the binding goes out of scope). – user4815162342 Feb 02 '21 at 22:21

3 Answers3

3

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.

Ultrasaurus
  • 3,031
  • 2
  • 33
  • 52
2

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;
}
Jmb
  • 18,893
  • 2
  • 28
  • 55
1

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

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98