2017-06-07 3 views
3

私は一般的な構文と錆を学ぶためにいくつかの基本的なデータ構造を開発しています。ここで私は、スタックのために思い付いたものです:`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が不変の参照を取ることを知っています。もし私がそれを変更可能なものに切り替えると、私はちょうど2番目の変更可能な借用を取得します。

答えて

6

あなたpop関数が宣言されているとおり:だから、あなたは、あなたが自動的に推論することが明示的に&strの有効期間を指定し、mut selfへの参照のために生涯をさせる必要があり、静的な文字列を返すようにしたい

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

によりlifetime elisionに、これは

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

に展開これは、番目のことを言いますe Result::Err variantは、呼び出し元のスタックと同じ長さの文字列です。入力と出力の生存時間が同じであるため、戻り値はStackデータ構造のどこかを指している可能性があります。そのため、戻り値は借用を保持し続ける必要があります。 expectが今までの変数バインディングにそれを入れずErrバリアントを破棄、Resultを消費するためです

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

。それは保存されていないので、借用はどこにも保存することができず、解放されます。

この問題を解決するには、入力参照と出力参照の間に明確な有効期間が必要です。

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

エクストラノート:

  • はの終わりに明示的returnを呼び出さないでください。あなたは、文字列リテラルを使用しているので、最も簡単な解決策は'static寿命を使用していることを示すためにありますブロック/方法:return Result::Ok(last) =>Result::Ok(last)
  • ResultResult::Ok、およびResult::Errはすべてthe preludeを経由して輸入されているので、あなたがそれらを修飾する必要はありません:Result::Ok(last) =>Ok(last)
  • 多くの場合、タイプを指定する必要はありませんlet len: usize = self.data.len() =>let len = self.data.len()です。
+0

@MatthieuM。良い点;私のところでは怠け者。私は更新しました。 – Shepmaster

+0

返品の変更に何か変更がありますか?私は個人的な好みのためにそれを行うだけでなく、変数をタイプするので、私が戻り値の型を適切に理解すれば、今のところわかります。 –

+0

@SamuelYvonいいえ、私の提案は何も変わりません。あなたは何でもしたいことができますが、**あなたがいる言語のコーディング規約に従うことを強くお勧めします**他の錆コード、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"); 
    } 
} 

です。

pub fn pop(&mut self) -> Result<T, &'static str> { 
関連する問題