2015-12-20 13 views
5

パラメータ関数に渡され、変更することができる。関数パラメータであるスライスを変更するにはどうすればよいですか?

fn set_42(int: &mut i32) { 
    *int += 42; 
} 

fn main() { 
    let mut int = 0; 
    set_42(&mut int); 
    println!("{:?}", int); 
} 

Output:単純スライスを使用するようにコードを変更

42 

エラーの全体の束で失敗:

fn pop_front(slice: &mut [i32]) { 
    *slice = &{slice}[1..]; 
} 

fn main() { 
    let mut slice = &[0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output

<anon>:2:14: 2:27 error: mismatched types: 
expected `[i32]`, 
    found `&[i32]` 
(expected slice, 
    found &-ptr) [E0308] 
<anon>:2  *slice = &{slice}[1..]; 
         ^~~~~~~~~~~~~ 
<anon>:2:14: 2:27 help: see the detailed explanation for E0308 
<anon>:2:5: 2:11 error: the trait `core::marker::Sized` is not implemented for the type `[i32]` [E0277] 
<anon>:2  *slice = &{slice}[1..]; 
      ^~~~~~ 
<anon>:2:5: 2:11 help: see the detailed explanation for E0277 
<anon>:2:5: 2:11 note: `[i32]` does not have a constant size known at compile-time 
<anon>:2:5: 2:11 note: the left-hand-side of an assignment must have a statically known size 
error: aborting due to 2 previous errors 

私たちは、私が本当に欲しいものではありません可変スライスを(使用してみた場合、

fn pop_front(mut slice: &mut [i32]) { 
    slice = &mut {slice}[1..]; 
} 

fn main() { 
    let mut slice = &mut [0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 
:私はちょうどそれが要素の狭い範囲)と可変パラメータをカバーしていて、スライス自体を変更したい、スライス内の値を変更したくない、それは元のスライスには影響しません

Output

[0, 1, 2, 3] 

質問:関数のパラメータだスライスを修正する方法はありますか?私はスライス内の要素を変更したくありません。私はちょうどスライス自体の範囲を変更して、より小さな「サブスライス」にしたいと思っています。

答えて

5

、ここでの核となるアイデアは&mut &... [T]...mutまたは空である場合)を取り、内部スライスへの読み取り/書き込みにあります。他の答えは、安全なコードで&mut &[T]が可能であり、安全でないコードで&mut &mut [T]が可能であることを示していますが、なぜ違いがあるのか​​説明していません... &mut &mut [T]も安全なコードで可能です。明示的な一生の用語では

は、ネストされた参照は、いくつかの寿命'aと 'bため&'a mut &'b ... [T]のようなもので、ここでの目標は、&'b ... [T]を取得し、それをスライスし、&'a mutにそれを書くことです。 &'a mut &'b [T]については

が、これは簡単です:&[T]はとても効果的&mutのうち、&'b [T]をコピーして、後で、短い方で既存の値が上書きされます*slice = &slice[1..]を書いて、コピーです。コピーとは、文字通り&'b [T]と操作することを意味するので、それと&'a mutとの間に直接のつながりはないので、突然変異することは合法です。これは、効果的に&'a mut &'b mut [T]ものですについては

fn pop_front<'a, 'b>(slice: &'a mut &'b[i32]) { 
    // *slice = &slice[1..] behaves like 
    let value: &'b [i32] = *slice; 
    *slice = &value[1..] 
} 

(私は寿命を標識し、私の説明に結びつけるタイプを注釈付きますが、コードが機能するために、これは必要ありませんしました。)

のようなものです小さな参照番号:&mut [T]はコピーできません。逆参照はコピーされません。&'a mut [T]を与えるように再連結されます。すなわち、スライスの寿命はの外側&'a mutに接続され、内部の&'b mut [T]ではありません。これは、スライスされた参照が上書きしようとしている型よりも寿命が短いことを意味します。したがって、その位置にスライスを格納することはできません。言い換えれば:

fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) { 
    let value: &'a mut [i32] = &mut **slice; 
    *slice = &mut value[1..] // error 
} 

&'a mut &'b mut [T]ために安全にこれを行う方法は、その'b寿命の参照のうち、内部のスライスを取得することです。これには、「1人の所有者」ルールを追跡し、借用することなく、この種の所有権操作のための最良の機能はmem::replaceです。これは、内部の&'b mut [T]をいくつかのプレースホルダーと交換することで抽出することができます。このプレースホルダーをショートバージョンで上書きすることができます。最高の/唯一のプレースホルダは空の配列です。&mut []は、タイプXと任意の生涯'cの場合は&'c mut [X]となる可能性があります。保存するデータがないため、初期化が必要ないため、データが無効になることはありません。特に、それは&'b mut [T]ことができます:

fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) { 
    let value: &'b mut [i32] = mem::replace(slice, &mut []); 
    *slice = &mut value[1..] 
} 

(上記の通り、私は必要以上のものがより明確作った。)

+0

詳しい説明と寿命の注釈についてはありがとうございます。それは信じられないほど役に立つ! – Cornstalks

3

不変スライスを変更する必要がある場合は、see Cornstalks's answerです。

安全な錆の可変スライスを変更することはできません。可変スライスのサブスライスを取ると、元のスライスから効果的に借用することができます。これは、サブスライスが元のスライスよりも長く存続してはならないことを意味します。

あなたはこのようなものとします

fn pop_front(slice: &mut &mut [i32]) { 
    *slice = &mut slice[1..]; 
} 

をしかしsubslice slice[1..]は、関数の最後までのみ有効であり、その時点ボローが終わると元のスライス(sliceパラメータ)になりますもう一度使える。

use std::slice; 

fn pop_front(slice: &mut &mut [i32]) { 
    let ptr = slice.as_mut_ptr(); 
    let len = slice.len(); 
    *slice = unsafe { slice::from_raw_parts_mut(ptr.offset(1), len - 1) }; 
} 

fn main() { 
    let mut slice = &mut [0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

playground

このプログラムの出力:

私たちは手動で私たちが望むのスライスを構築するために、いくつかのunsafeコードを使用することができます

[1, 2, 3] 
3

Francis Gagné's answerの一部を使用して(私はしませんでした試してみてください&mut &)、私はunsafeコードを使用せずに働くことができました:

fn pop_front(mut slice: &mut &[i32]) { 
    *slice = &slice[1..]; 
} 

fn main() { 
    let mut slice = &[0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:他の人が言ったように

[1, 2, 3] 
+0

これは不変のスライスではなく、変更可能なスライスを変異させます。 OPのニーズは何だったのかはっきりしていません... –

+0

@FrancisGagné:これは私の質問で尋ねようとしていたものですが、正直言って私の実際のコードでは可変スライスを使用しています。そこに役立つ。私は難しい変更可能な不変のスライスについて分かりやすく話してみたいと思っていました。なぜなら、「変更可能な不変」は無意味に聞こえるからです(しかし、おそらく正しい言葉です)。 – Cornstalks

+0

あなたの 'pop_front'は、変更可能な*スライス*に変更可能な*参照*をとります。私の 'pop_front'は可変スライスへの可変参照を取ります。 –

関連する問題