2017-06-07 42 views
3

我正在開發一些基本的數據結構來學習一般的語法和Rust。以下是我想出了一個棧:爲什麼要調用`fn pop(&mut self) - >結果<T, &str>`繼續借用我的數據結構?

#[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需要一個不可變的引用,如果我將它切換爲可變的,我只是得到第二個可變的借位。

回答

6

pop函數聲明爲:

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

由於lifetime elision,這擴展到

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

這表示日e Result::Err變體是一個字符串,只要您調用它的堆棧就會存在。由於輸入和輸出的生命週期是相同的,所以返回的值可能指向數據結構的某處,所以返回的值必須繼續保持借用。

If I add a .expect() after it, it is ok, it doesn't trigger that error.

這是因爲expect消耗Result,丟棄Err變型而沒有把它變成一個變量綁定。由於從未存儲過,因此借用無法在任何地方保存並被釋放。

要解決該問題,您需要在輸入參考和輸出參考之間有不同的生命週期。由於您使用一個字符串字面量,最簡單的解決方案是指,使用'static壽命:

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

附加說明:

  • 請勿的末尾調用return明確塊/方法:return Result::Ok(last) =>Result::Ok(last)
  • ResultResult::OkResult::Err通過the prelude全部進口,所以你不需要限定它們:Result::Ok(last) =>Ok(last)
  • 在許多情況下不需要指定類型let len: usize = self.data.len() =>let len = self.data.len()
+0

@MatthieuM。好點;我身上的sl iness。我已更新。 – Shepmaster

+0

添加返回值是否改變了什麼?我個人喜好,以及輸入變量,所以我現在知道如果我正確理解返回類型。 –

+0

@SamuelYvon不,我的建議沒有改變任何東西。你可以做任何你想做的事,但總是**強烈推薦**來遵守你所使用的語言的編碼約定。像其他Rust代碼一樣編寫Rust代碼,像其他JS代碼一樣編寫JS代碼,像其他Go代碼一樣使用Go代碼,Ruby代碼像其他Ruby代碼,等等。 – Shepmaster

4

這是因爲lifetimes。當構造將參考的方法編譯器檢測到,如果沒有指定它的壽命「產生」他們:

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> { 
相關問題