Wie kann ich das Feld einer Struktur von einer Methode mutieren?

73

Ich möchte das machen:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&self) {
        self.y += 1;
    }
}

fn main() {
    let p = Point { x: 0, y: 0 };
    p.up();
}

Dieser Code löst jedoch einen Compilerfehler aus:

error[E0594]: cannot assign to field `self.y` of immutable binding
 --> src/main.rs:8:9
  |
7 |     fn up(&self) {
  |           ----- use `&mut self` here to make mutable
8 |         self.y += 1;
  |         ^^^^^^^^^^^ cannot mutably borrow field of immutable binding
alxkolm
quelle

Antworten:

108

Sie müssen &mut selfanstelle von &selfund die pVariable veränderbar machen:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&mut self) {
        // ^^^ Here
        self.y += 1;
    }
}

fn main() {
    let mut p = Point { x: 0, y: 0 };
    //  ^^^ And here
    p.up();
}

In Rust wird die Veränderlichkeit vererbt: Der Eigentümer der Daten entscheidet, ob der Wert veränderlich ist oder nicht. Referenzen implizieren jedoch kein Eigentum und können daher selbst unveränderlich oder veränderlich sein. Sie sollten das offizielle Buch lesen, in dem alle diese Grundkonzepte erläutert werden.

Vladimir Matveev
quelle
35
@VladimirMatveev Ich möchte nur sagen, dass selbst wenn Sie das Buch gelesen und durchgearbeitet haben und diese Konzepte noch nie zuvor gesehen haben, es möglicherweise nicht viel Sinn macht, bis Sie in eine tatsächliche Situation geraten, in der es relevant ist. Wie es für mich war, so sind diese Antworten immer noch sehr hilfreich;)
Aeolun
6
@Aeolun - Sehr gut gesagt. Ich ging das Buch durch und dachte, ich hätte das Konzept verstanden, stellte aber fest, dass ich es wirklich nicht tat, bis ich anfing, an einem tatsächlichen Rust-Projekt zu arbeiten.
Syndog
3

Mithilfe von können Cell<T>Sie die Veränderbarkeit auf Feldebene emulieren:

use std::cell::Cell;

struct Point {
    x: i32,
    y: Cell<i32>,
}

impl Point {
    fn up(&self) {
        self.y.set(self.y.get() + 1);
    }
}

fn main() {
    let p = Point { x: 0, y: Cell::new(0) };
    p.up();
    println!("y: {:?}", p.y);
}

Dies wird gedruckt y: Cell { value: 7 }und wir haben erfolgreich aktualisiert y.

Wenn Sie den nightlyKanal verwenden, können Sie außerdem #![feature(cell_update)]über Ihrer .rsDatei deklarieren und die folgende Syntax in Ihrer up()Methode verwenden:

impl Point {
    fn up(&self) {
        self.y.update(|x| x + 1);
    }
}

Hinweis: Diese Funktion ist eine experimentelle API nur für die Nacht.

Aus der Programmiersprache Rust bei Rust 1.7 .

Silvioprog
quelle
Gilt das auch für Box, Rc, Arc?
Michael Allwright
Ja tut es. Und Sie können sie kombinieren, zum Beispiel: Rc<Cell<i32>>... y: Rc::new(Cell::new(0)),.
Silvioprog