【问题标题】:Rust borrow checker throwing error in if statementRust 借用检查器在 if 语句中抛出错误
【发布时间】:2021-02-02 22:03:58
【问题描述】:

我正在通过一些 LeetCode 挑战来加深对 Rust 的理解。我正在尝试编写以下程序,该程序接受i32 输入,将其转换为String,反转数字并返回i32 数字。

在负数的情况下,例如-132,当数字反转时,连字符必须从堆栈中弹出:-132 -> 231- -> 231

我已经编写了以下代码,但遇到了借用检查器,有人可以帮忙吗?

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

这是playground中重现的错误

【问题讨论】:

  • 请在问题中包含错误信息。另外,请说明您是否阅读了整个错误(不仅仅是标记线上的部分)。如果是这样,您是否尝试理解并实施编译器的建议?
  • @user4815162342 是的,肯定地阅读了整个错误几次,我来回尝试使reversed 成为对x 的引用而不是直接引用该值,但会不断遇到各种各样的问题的错误。我已经用错误更新了问题。
  • 好的,所以编译器告诉您“值的寿命不够长”,并提示“考虑使用let 绑定来创建寿命更长的值”。它表明x.to_string() 创建的临时值不够长。您可以从let s = x.to_string()(编译器引用的let 绑定)开始,然后改用s。这允许 reversed 迭代器引用实际存在的值所拥有的字符(因为它在 let 绑定中,保证在绑定超出范围之前一直存在)。

标签: rust reference lifetime borrow-checker borrowing


【解决方案1】:

playground中转载的原始错误

这是一个与您修复错误的方法接近的解决方案:

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()
}

工作版本:playground

这个other post 很好地解释了原因。在这个问题的背景下:

 x.to_string().chars().rev();
//    ^         ^
//    String <- &str

to_string返回一个String,但是这段语句后面的代码并没有引用那个String,所以需要释放String,但是迭代器引用了chars()中的&amp;str然后它成为对不再存在的事物的引用。通过将reversed 的类型更改为String 并使用collect,Rust 可以将新数据绑定到局部变量,而不必在语句末尾删除它。

【讨论】:

    【解决方案2】:

    LeetCode 挑战是否强制进行 int→string→int 转换?我会直接在整数上做:

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

    【讨论】:

      【解决方案3】:

      为什么不直接取 x 的绝对值,然后再将其转换为 String,这样您就不必处理连字符边缘情况?

      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


      即使我们将输入作为 String 并必须处理连字符,最惯用的解决方案是 filter() 输出:

      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

      【讨论】:

      • 这完全是一个很好的答案,但我自己的部分练习是了解借用检查机制,但谢谢!
      猜你喜欢
      • 2015-01-05
      • 1970-01-01
      • 2022-01-11
      • 2022-11-11
      • 2018-03-01
      • 2018-01-12
      • 1970-01-01
      • 2022-11-30
      • 2017-10-28
      相关资源
      最近更新 更多