ごらくらいふ

プログラミングしたりゲームしたり

列挙型で error[E0507]: cannot move out of borrowed content に対処した

ムーブセマンティクスを忘れていた話。

Builderパターンで以下のように書いたところコンパイラerror[E0507]: cannot move out of borrowed contentと怒られた。

enum Method {
    Get,
    Post,
}
struct Client {
    method: Method,
}
struct ClientBuilder {
    method: Method,
}
impl ClientBuilder {
    fn new() -> Self {
        ClientBuilder {
            method: Method::Get,
        }
    }
    fn method(&mut self, method: Method) -> &mut Self {
        self.method = method;
        self
    }
    fn finalize(&self) -> Client {
        Client {
            method: self.method,
        }
    }
}

ClientBuilder.finalize()で、self.methodの所有権を失ってしまうために怒られていた。

対応

列挙型MethodCopyトレイトを実装することで解決できる。そのためのアトリビュートもある。

#[derive(Copy, Clone)]
enum Method {
    Get,
    Post,
}

認識の修正

この対処方法はドキュメントにも書いてある

このケースは、以下の認識で書いたために引っかかってしまった。

  • enumの中身は数値だろう、すなわち、プリミティブな値だろう
  • プリミティブな値であればコピーされて所有権を失わないだろう

この認識を改めていく。

enumの中身はプリミティブな値ではない

ドキュメント 列挙型 より

列挙型の値(ヴァリアントと呼ぶ)は構造体の定義である。 Unit-like構造体であったり、名前付きフィールドを持つ構造体であったり、タプル構造体であったり。

enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

そもそもプリミティブな値だから所有権を失わないのではない

ドキュメント 所有権 より

全てのプリミティブ型は Copy トレイトを実装しているので、推測どおりそれらの所有権は「所有権ルール」に従ってはムーブしません。

Copyトレイトを実装しているから、所有権を失わない。

所感

見事にドキュメントで説明される事柄を踏み抜いた