为什么要调用`fn pop(&amp; mut self) - &gt;结果<t,&str =“”>`继续借用我的数据结构?

时间:2017-06-07 13:48:33

标签: rust borrow-checker

我正在开发一些基本的数据结构来学习语法和Rust。以下是我为堆栈提出的建议:

#[allow(dead_code)]
mod stack {
    pub struct Stack<T> {
        data: Vec<T>,
    }

    impl<T> Stack<T> {
        pub fn new() -> Stack<T> {
            return Stack { data: Vec::new() };
        }

        pub fn pop(&mut self) -> Result<T, &str> {
            let len: usize = self.data.len();

            if len > 0 {
                let idx_to_rmv: usize = len - 1;
                let last: T = self.data.remove(idx_to_rmv);
                return Result::Ok(last);
            } else {
                return Result::Err("Empty stack");
            }
        }

        pub fn push(&mut self, elem: T) {
            self.data.push(elem);
        }

        pub fn is_empty(&self) -> bool {
            return self.data.len() == 0;
        }
    }
}

mod stack_tests {
    use super::stack::Stack;

    #[test]
    fn basics() {
        let mut s: Stack<i16> = Stack::new();

        s.push(16);
        s.push(27);

        let pop_result = s.pop().expect("");

        assert_eq!(s.pop().expect("Empty stack"), 27);
        assert_eq!(s.pop().expect("Empty stack"), 16);

        let pop_empty_result = s.pop();

        match pop_empty_result {
            Ok(_) => panic!("Should have had no result"),
            Err(_) => {
                println!("Empty stack");
            }
        }

        if s.is_empty() {
            println!("O");
        }
    }
}

我收到了这个有趣的错误:

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
  --> src/main.rs:58:12
   |
49 |         let pop_empty_result = s.pop();
   |                                - mutable borrow occurs here
...
58 |         if s.is_empty() {
   |            ^ immutable borrow occurs here
...
61 |     }
   |     - mutable borrow ends here

为什么我不能在我的可变结构上调用pop

为什么pop借用了这个值?如果我在它之后添加.expect(),那就没关系,它不会触发该错误。我知道is_empty采用不可变引用,如果我将其切换为可变,我只需要获得第二次可变借用。

2 个答案:

答案 0 :(得分:7)

您的pop函数声明为:

pub fn pop(&mut self) -> Result<T, &str>

由于lifetime elision,这会扩展为

pub fn pop<'a>(&'a mut self) -> Result<T, &'a str>

这表示Result::Err变体是一个字符串,只要您调用它的堆栈就存在。由于输入和输出生命周期相同,返回的值可能指向Stack数据结构的某处,因此返回的值必须继续保持借用。

  

如果我在它之后添加.expect(),那就没问题,它不会触发该错误。

这是因为expect使用Result,丢弃Err变体而不将其置于变量绑定中。由于从未存储过,借用不能保存在任何地方而且会被释放。

要解决此问题,您需要在输入引用和输出引用之间具有不同的生命周期。由于您使用的是字符串文字,因此最简单的解决方案是使用'static生命周期来表示:

pub fn pop(&mut self) -> Result<T, &'static str>

额外说明:

  • 请勿在阻止/方法结束时明确致电returnreturn Result::Ok(last) =&gt; Result::Ok(last)
  • ResultResult::OkResult::Err都是通过the prelude导入的,因此您无需对其进行限定:Result::Ok(last) =&gt; Ok(last)
  • 在许多情况下,无需指定类型let len: usize = self.data.len() =&gt; let len = self.data.len()

答案 1 :(得分:4)

这是因为lifetimes。当你构造一个接受引用的方法时,编译器会检测到它,如果没有指定生命周期,它就会生成&#34;它们:

pub fn pop<'a>(&'a mut self) -> Result<T, &'a str> {
    let len: usize = self.data.len();

    if len > 0 {
        let idx_to_rmv: usize = len - 1;
        let last: T = self.data.remove(idx_to_rmv);
        return Result::Ok(last);
    } else {
        return Result::Err("Empty stack");
    }
}

这是编译器实际看到的。因此,您希望返回一个静态字符串,然后您必须明确指定&str的生命周期,并自动推断mut self引用的生命周期:

pub fn pop(&mut self) -> Result<T, &'static str> {