9

変更可能な借用の値を置き換えようとしています。その一部を新しい値に移動します。

enum Foo<T> {
    Bar(T),
    Baz(T),
}

impl<T> Foo<T> {
    fn switch(&mut self) {
        *self = match self {
            &mut Foo::Bar(val) => Foo::Baz(val),
            &mut Foo::Baz(val) => Foo::Bar(val),
        }
    }
}

上記のコードは機能しません。当然のことながら、値を から移動すると、selfその整合性が損なわれます。しかし、その値はその後すぐに削除されるため、(コンパイラではないにしても) その安全性を保証できます。

これを達成する方法はありますか?これは安全でないコードの仕事のように感じますが、それがどのように機能するかはわかりません。

4

3 に答える 3

5

上記のコードは機能しません。当然のことながら、値を self から移動すると、その整合性が損なわれます。

これはまさにここで起こることではありません。たとえば、次の場合と同じことselfがうまく機能します。

impl<T> Foo<T> {
    fn switch(self) {
        self = match self {
            Foo::Bar(val) => Foo::Baz(val),
            Foo::Baz(val) => Foo::Bar(val),
        }
    }
}

錆は、部分的および全体的な動きでまったく問題ありません。ここでの問題は、移動しようとしている値を所有していないことです。変更可能な借用参照しかありません。変更可能な参照を含め、参照から移動することはできません。

実際、これは頻繁に要求される機能の 1 つであり、この機能から移動できる特別な種類の参照です。いくつかの種類の有用なパターンが可能になります。詳細については、こちらこちらをご覧ください。

それまでの間、場合によっては と を使用できstd::mem::replaceますstd::mem::swap。これらの関数を使用すると、変更可能な参照から値を「取得」することができます。

于 2015-04-10T22:01:38.450 に答える
4

さて、私は少しunsafeネスとstd::mem.

self初期化されていない一時的な値に置き換えます。以前は だったものを「所有」するようになったのでself、安全に値を移動して置き換えることができます。

use std::mem;

enum Foo<T> {
    Bar(T),
    Baz(T),
}

impl<T> Foo<T> {
    fn switch(&mut self) {
        // This is safe since we will overwrite it without ever reading it.
        let tmp = mem::replace(self, unsafe { mem::uninitialized() });
        // We absolutely must **never** panic while the uninitialized value is around!

        let new = match tmp {
            Foo::Bar(val) => Foo::Baz(val),
            Foo::Baz(val) => Foo::Bar(val),
        };

        let uninitialized = mem::replace(self, new);
        mem::forget(uninitialized);
    }
}

fn main() {}
于 2015-04-10T22:05:30.280 に答える