Was ist der Unterschied zwischen dem Platzieren von "mut" vor einem Variablennamen und nach dem ":"?

77

Hier sind zwei Funktionssignaturen, die ich in der Rust-Dokumentation gesehen habe:

fn modify_foo(mut foo: Box<i32>) { *foo += 1; *foo }
fn modify_foo(foo: &mut i32) { *foo += 1; *foo }

Warum die unterschiedliche Platzierung von mut?

Es scheint, dass die erste Funktion auch als deklariert werden könnte

fn modify_foo(foo: mut Box<i32>) { /* ... */ }
Jimmy Lu
quelle
Für C ++ - Programmierer: Der Unterschied ist ähnlich wie bei Zeiger constund pointeeKonstante .
legends2k

Antworten:

86

mut foo: Tbedeutet, dass Sie eine Variable namens fooa haben T. Sie können ändern, worauf sich die Variable bezieht :

let mut val1 = 2;
val1 = 3; // OK

let val2 = 2;
val2 = 3; // error: re-assignment of immutable variable

Auf diese Weise können Sie auch Felder einer Struktur ändern, die Sie besitzen:

struct Monster { health: u8 }

let mut orc = Monster { health: 93 };
orc.health -= 54;

let goblin = Monster { health: 28 };
goblin.health += 10; // error: cannot assign to immutable field

foo: &mut Tbedeutet, dass Sie eine Variable haben, die auf ( &) einen Wert verweist, und Sie dürfen ( mut) den referenzierten Wert ändern (einschließlich Felder, wenn es sich um eine Struktur handelt):

let val1 = &mut 2;
*val1 = 3; // OK

let val2 = &2;
*val2 = 3; // error: cannot assign to immutable borrowed content

Beachten Sie, dass dies &mutnur mit einer Referenz sinnvoll foo: mut Tist - keine gültige Syntax. Sie können die beiden Qualifikationsmerkmale ( let mut a: &mut T) auch kombinieren , wenn dies sinnvoll ist.

Shepmaster
quelle
11
Aha. Ich denke , es ist wie in C ++ , wo Sie können int const*gegen int *constverschiedene Dinge zu erreichen.
Jimmy Lu
3
@Shepmaster Möglicherweise möchten Sie hinzufügen, dass Sie mutbei einer Bindung innerhalb der Struktur mutieren können (wenn es sich um eine Struktur handelt).
Scott Olson
7
@BeyondSora Nicht &mut Typeals &(mut Type), sondern als denken (&mut) Type. Das Schlüsselwort mutwird in Typen im Allgemeinen nicht verwendet, es wird jedoch eine Referenzart aufgerufen &mut.
Scott Olson
2
@BeyondSora Sie können die neueste Bearbeitung der obigen Antwort sehen. Die grundlegende Erklärung ist, wenn Sie eine Struktur mutieren können, können Sie so weit in die Struktur mutieren, wie Sie möchten (ihre Felder, ihre Felder usw.). Es gibt keine constFelder. Dies ist sicher, da Rust garantiert, wenn Sie etwas mutieren können, niemand anderes es gleichzeitig lesen oder mutieren kann.
Scott Olson
2
@idierc Ja. Sie können sich &Tund &mut Tals Zucker für Ref<T>und RefMut<T>vorstellen (Typen, die ich gerade erfunden habe).
Scott Olson
92

Wenn Sie aus C / C ++ kommen, kann es auch hilfreich sein, sich das grundsätzlich so vorzustellen:

// Rust          C/C++
    a: &T     == const T* const a; // can't mutate either
mut a: &T     == const T* a;       // can't mutate what is pointed to
    a: &mut T == T* const a;       // can't mutate pointer
mut a: &mut T == T* a;             // can mutate both

Sie werden feststellen, dass dies Umkehrungen voneinander sind. C / C ++ verfolgt einen "Blacklist" -Ansatz. Wenn Sie möchten, dass etwas unveränderlich ist, müssen Sie dies explizit sagen, während Rust einen "Whitelist" -Ansatz verwendet. Wenn Sie möchten, dass etwas veränderlich ist, müssen Sie dies explizit sagen.

anderspitman
quelle
3
Dies ist ein großartiger Tisch. Es kann sinnvoll sein zu beachten, dass &mut TVerweise auch analog zu T* restrictZeigern in C sind: Sie dürfen nicht mit einem Alias ​​versehen sein. &TReferenzen haben keine solche Einschränkung und es gibt keinen Referenztyp analog zu nicht restrictqualifizierten T*Zeigern.
Trentcl
1
Ich habe keinen C-Hintergrund, aber ich denke immer noch, dass dies dies besser erklärt (mit den Kommentaren) als die akzeptierte Antwort. Manchmal ist einfacher besser als länger.
kres0345