Unveränderliches Objekt - Immutable object

In der objektorientierten und funktionalen Programmierung ist ein unveränderliches Objekt (unveränderliches Objekt) ein Objekt, dessen Zustand nach seiner Erstellung nicht mehr geändert werden kann. Dies steht im Gegensatz zu einem veränderlichen Objekt (änderbares Objekt), das nach seiner Erstellung geändert werden kann. In einigen Fällen wird ein Objekt als unveränderlich betrachtet, selbst wenn sich einige intern verwendete Attribute ändern, aber der Zustand des Objekts scheint aus externer Sicht unverändert zu sein. Beispielsweise könnte ein Objekt, das Memoization verwendet, um die Ergebnisse teurer Berechnungen zwischenzuspeichern, immer noch als unveränderliches Objekt betrachtet werden.

Strings und andere konkrete Objekte werden typischerweise als unveränderliche Objekte ausgedrückt, um die Lesbarkeit und Laufzeiteffizienz in der objektorientierten Programmierung zu verbessern . Unveränderliche Objekte sind auch nützlich, da sie von Natur aus threadsicher sind . Weitere Vorteile sind, dass sie einfacher zu verstehen und zu begründen sind und eine höhere Sicherheit bieten als veränderliche Objekte.

Konzepte

Unveränderliche Variablen

Bei der imperativen Programmierung werden Werte, die in Programmvariablen enthalten sind, deren Inhalt sich nie ändert, als Konstanten bezeichnet , um sie von Variablen zu unterscheiden, die während der Ausführung geändert werden könnten. Beispiele sind Umrechnungsfaktoren von Metern in Fuß oder der Wert von pi auf mehrere Dezimalstellen.

Schreibgeschützte Felder können während des Programmablaufs berechnet werden (im Gegensatz zu vorher bekannten Konstanten), ändern sich jedoch nach der Initialisierung nicht mehr.

Schwache vs. starke Unveränderlichkeit

Manchmal spricht man davon, dass bestimmte Felder eines Objekts unveränderlich sind. Dies bedeutet, dass es keine Möglichkeit gibt, diese Teile des Objektzustands zu ändern, obwohl andere Teile des Objekts möglicherweise änderbar sind ( schwach unveränderlich ). Wenn alle Felder unveränderlich sind, ist das Objekt unveränderlich. Wenn das gesamte Objekt nicht durch eine andere Klasse erweitert werden kann, wird das Objekt als stark unveränderlich bezeichnet . Dies könnte beispielsweise dazu beitragen, bestimmte Invarianten über bestimmte Daten im Objekt explizit zu erzwingen, die während der Lebensdauer des Objekts gleich bleiben. In einigen Sprachen geschieht dies mit einem Schlüsselwort (zB constin C++ , finalin Java ), das das Feld als unveränderlich bezeichnet. Einige Sprachen kehren es um: In OCaml sind Felder eines Objekts oder Datensatzes standardmäßig unveränderlich und müssen explizit mit gekennzeichnet mutablewerden.

Verweise auf Objekte

In den meisten objektorientierten Sprachen kann mit Referenzen auf Objekte verwiesen werden . Einige Beispiele für solche Sprachen sind Java , C++ , C# , VB.NET und viele Skriptsprachen wie Perl , Python und Ruby . In diesem Fall ist es wichtig, ob der Zustand eines Objekts variieren kann, wenn Objekte über Referenzen geteilt werden.

Referenzieren vs. Kopieren von Objekten

Wenn ein Objekt als unveränderlich bekannt ist, wird es bevorzugt, eine Referenz darauf zu erstellen , anstatt das gesamte Objekt zu kopieren. Dies geschieht, um Speicher zu sparen, indem Datenduplizierung verhindert und Aufrufe an Konstruktoren und Destruktoren vermieden werden. es führt auch zu einer potenziellen Steigerung der Ausführungsgeschwindigkeit.

Die Referenzkopiertechnik ist für veränderliche Objekte viel schwieriger zu verwenden, da, wenn ein Benutzer einer veränderlichen Objektreferenz diese ändert, alle anderen Benutzer dieser Referenz die Änderung sehen. Wenn dies nicht der beabsichtigte Effekt ist, kann es schwierig sein, die anderen Benutzer zu benachrichtigen, damit sie richtig reagieren. In diesen Situationen ist das defensive Kopieren des gesamten Objekts anstelle der Referenz normalerweise eine einfache, aber kostspielige Lösung. Das Beobachtermuster ist eine alternative Technik zur Handhabung von Änderungen an veränderlichen Objekten.

Kopieren beim Schreiben

Eine Technik, die die Vorteile von veränderlichen und unveränderlichen Objekten vereint und von fast jeder modernen Hardware direkt unterstützt wird, ist Copy-on-Write (COW). Wenn ein Benutzer bei dieser Technik das System auffordert, ein Objekt zu kopieren, erstellt es stattdessen lediglich eine neue Referenz, die immer noch auf dasselbe Objekt verweist. Sobald ein Benutzer versucht, das Objekt durch eine bestimmte Referenz zu modifizieren, erstellt das System eine echte Kopie, wendet die Modifikation darauf an und setzt die Referenz so, dass sie auf die neue Kopie verweist. Die anderen Benutzer sind davon nicht betroffen, da sie noch auf das Originalobjekt verweisen. Daher scheinen alle Benutzer unter COW über eine veränderliche Version ihrer Objekte zu verfügen, obwohl für den Fall, dass Benutzer ihre Objekte nicht ändern, die Platzspar- und Geschwindigkeitsvorteile unveränderlicher Objekte erhalten bleiben. Copy-on-write ist in virtuellen Speichersystemen beliebt , weil es ihnen erlaubt, Speicherplatz zu sparen, während sie dennoch alles, was ein Anwendungsprogramm tun könnte, korrekt handhaben.

Praktikum

Die Praxis, immer Referenzen anstelle von Kopien gleicher Objekte zu verwenden, wird als Interning bezeichnet . Wenn Interning verwendet wird, werden zwei Objekte genau dann als gleich betrachtet, wenn ihre Referenzen, die normalerweise als Zeiger oder ganze Zahlen dargestellt werden, gleich sind. Einige Sprachen tun dies automatisch: Python beispielsweise interniert automatisch kurze Zeichenfolgen . Wenn der Algorithmus, der die Internierung implementiert, dies in jedem Fall gewährleistet, in dem dies möglich ist, reduziert sich der Vergleich von Objekten auf Gleichheit auf den Vergleich ihrer Zeiger – ein erheblicher Geschwindigkeitsgewinn in den meisten Anwendungen. (Auch wenn der Algorithmus nicht garantiert umfassend ist, besteht immer noch die Möglichkeit einer Fast-Path- Case-Verbesserung, wenn die Objekte gleich sind und dieselbe Referenz verwenden.) Interning ist im Allgemeinen nur für unveränderliche Objekte nützlich.

Fadensicherheit

Unveränderliche Objekte können in Multithread-Anwendungen nützlich sein. Mehrere Threads können auf Daten einwirken, die durch unveränderliche Objekte dargestellt werden, ohne sich Sorgen zu machen, dass die Daten von anderen Threads geändert werden. Unveränderliche Objekte gelten daher als threadsicherer als veränderliche Objekte.

Verletzung der Unveränderlichkeit

Unveränderlichkeit bedeutet nicht , dass das Objekt als in dem Computer gespeichert Speichern unwriteable ist. Vielmehr ist Unveränderlichkeit ein Kompilierung- Konstrukt , das ein Programmierer gibt an, welche durch die normale Schnittstelle des Objekts tun, nicht unbedingt das, was sie unbedingt tun kann (zum Beispiel durch die Art System zu umgehen oder zu verletzen const Korrektheit in C oder C ++ ).

Sprachspezifische Details

In Python , Java und .NET Framework sind Zeichenfolgen unveränderliche Objekte. Sowohl Java als auch .NET Framework verfügen über veränderliche Versionen von string. In Java sind dies StringBufferund StringBuilder(veränderliche Versionen von Java String) und in .NET ist dies StringBuilder(veränderliche Version von .Net String). Python 3 hat eine veränderliche String-Variante (Bytes) namens bytearray.

Darüber hinaus sind alle primitiven Wrapper-Klassen in Java unveränderlich.

Ähnliche Muster sind das Immutable Interface und Immutable Wrapper .

In rein funktionalen Programmiersprachen ist es nicht möglich, veränderliche Objekte zu erstellen, ohne die Sprache zu erweitern (zB über eine veränderliche Referenzbibliothek oder eine fremde Funktionsschnittstelle ), daher sind alle Objekte unveränderlich.

Ada

In Ada wird jedes Objekt entweder als variabel (dh veränderbar; normalerweise der implizite Standard) oder constant(dh unveränderlich) über das constantSchlüsselwort deklariert.

  type Some_type is new Integer; -- could be anything more complicated
  x: constant Some_type:= 1; -- immutable
  y: Some_type; -- mutable

Unterprogrammparameter sind im In- Modus unveränderlich und im In-Out- und Out- Modus änderbar.

  procedure Do_it(a: in Integer; b: in out Integer; c: out Integer) is
  begin
    -- a is immutable
    b:= b + a;
    c:= a;
  end Do_it;

C#

In C# können Sie mit der readonlyAnweisung die Unveränderlichkeit der Felder einer Klasse erzwingen . Indem Sie alle Felder als unveränderlich erzwingen, erhalten Sie einen unveränderlichen Typ.

class AnImmutableType
{
    public readonly double _value;
    public AnImmutableType(double x) 
    { 
        _value = x; 
    }
    public AnImmutableType Square() 
    { 
        return new AnImmutableType(_value * _value); 
    }
}

C++

In C++ würde eine const-korrekte Implementierung von Cartes dem Benutzer ermöglichen, Instanzen der Klasse zu erstellen und sie dann je constnach Wunsch entweder (unveränderlich) oder veränderlich zu verwenden, indem zwei verschiedene Versionen der items()Methode bereitgestellt werden. (Beachten Sie, dass es in C++ nicht notwendig – und sogar unmöglich – ist, einen spezialisierten Konstruktor für constInstanzen bereitzustellen .)

class Cart {
 public:
  Cart(std::vector<Item> items): items_(items) {}

  std::vector<Item>& items() { return items_; }
  const std::vector<Item>& items() const { return items_; }

  int ComputeTotalCost() const { /* return sum of the prices */ }

 private:
  std::vector<Item> items_;
};

Beachten Sie, dass es möglich ist, das Objekt, auf das verwiesen wird oder auf das nur innerhalb einer Nicht-const-Methode verwiesen wird, zu mutieren, wenn es einen Datenmember gibt, der ein Zeiger oder eine Referenz auf ein anderes Objekt ist.

C++ bietet auch abstrakte (im Gegensatz zu bitweiser) Unveränderlichkeit über das mutableSchlüsselwort, wodurch eine Membervariable innerhalb einer constMethode geändert werden kann.

class Cart {
 public:
  Cart(std::vector<Item> items): items_(items) {}

  const std::vector<Item>& items() const { return items_; }

  int ComputeTotalCost() const {
    if (total_cost_) {
      return *total_cost_;
    }

    int total_cost = 0;
    for (const auto& item : items_) {
      total_cost += item.Cost();
    }
    total_cost_ = total_cost;
    return total_cost;
  }

 private:
  std::vector<Item> items_;
  mutable std::optional<int> total_cost_;
};

D

In D gibt es zwei Typqualifizierer , constund immutable, für Variablen, die nicht geändert werden können. Im Gegensatz zu C++ const, Java finalund C# readonlysind sie transitiv und gelten rekursiv auf alles, was durch Referenzen einer solchen Variablen erreichbar ist. Der Unterschied zwischen constund immutableist, worauf sie zutreffen: constist eine Eigenschaft der Variablen: Es können rechtlich veränderliche Verweise auf referenzierte Werte existieren, dh der Wert kann sich tatsächlich ändern. Im Gegensatz dazu immutableist eine Eigenschaft des referenzierten Werts: Der Wert und alles, was von ihm transitiv erreichbar ist, können sich nicht ändern (ohne das Typsystem zu brechen, was zu undefiniertem Verhalten führt ). Jeder Verweis auf diesen Wert muss mit constoder markiert werden immutable. Im Grunde genommen für jeden unqualifizierten Typen T, const(T)die disjunkte Vereinigung von T(veränderbaren) und immutable(T).

class C {
  /*mutable*/ Object mField;
    const     Object cField;
    immutable Object iField;
}

Für ein veränderliches CObjekt mFieldkann in es geschrieben werden. Für ein const(C)Objekt mFieldkann es nicht geändert werden, es erbt const; iFieldist immer noch unveränderlich, da es die stärkere Garantie ist. Für an immutable(C)sind alle Felder unveränderlich.

In einer Funktion wie dieser:

void func(C m, const C c, immutable C i)
{ /* inside the braces */ }

Innerhalb der geschweiften Klammern kann csich auf dasselbe Objekt wie beziehen m, sodass sich auch Mutationen zu mindirekt ändern können c. Auch ckönnte auf das gleiche Objekt beziehen i, aber seitdem der Wert unveränderlich ist, gibt es keine Änderungen. Allerdings mund ibeziehen sich auf das gleiche Objekt nicht rechtlich kann.

In der Sprache der Garantien hat mutable keine Garantien (die Funktion kann das Objekt ändern), constist eine nur nach außen gerichtete Garantie dafür, dass die Funktion nichts ändert, und immutableist eine bidirektionale Garantie (die Funktion ändert den Wert nicht und der Aufrufer muss nicht ändern).

Werte, die durch direkte Zuweisung am Deklarationspunkt oder durch einen Konstruktor initialisiert werden constoder werden immutablemüssen .

Da constParameter vergessen, ob der Wert veränderlich war oder nicht, fungiert ein ähnliches Konstrukt, inout, gewissermaßen als Variable für Veränderlichkeitsinformationen. Eine Funktion vom Typ const(S) function(const(T))gibt const(S)typisierte Werte für veränderliche, konstante und unveränderliche Argumente zurück. Im Gegensatz dazu eine Funktion vom Typ inout(S) function(inout(T))gibt Sfür veränderbare TArgumente, const(S)für const(T)Werte und immutable(S)für immutable(T)Werte.

Das Umwandeln von unveränderlichen Werten in veränderliche Werte führt bei Änderungen zu undefiniertem Verhalten, selbst wenn der ursprüngliche Wert von einem veränderlichen Ursprung stammt. Das Umwandeln von veränderlichen Werten in unveränderlich kann legal sein, wenn danach keine veränderlichen Verweise mehr vorhanden sind. "Ein Ausdruck kann von veränderlich (...) in unveränderlich umgewandelt werden, wenn der Ausdruck eindeutig ist und alle Ausdrücke, auf die er sich transitiv bezieht, entweder eindeutig oder unveränderlich sind." Wenn der Compiler die Eindeutigkeit nicht nachweisen kann, kann das Casting explizit durchgeführt werden und es liegt am Programmierer, sicherzustellen, dass keine veränderlichen Referenzen vorhanden sind.

Der Typ stringist ein Alias ​​für immutable(char)[], dh ein getippter Speicherabschnitt unveränderlicher Zeichen. Das Erstellen von Teilstrings ist billig, da es nur einen Zeiger und ein Längenfeld kopiert und modifiziert, und sicher, da die zugrunde liegenden Daten nicht geändert werden können. Objekte vom Typ const(char)[]können sich auf Strings, aber auch auf veränderliche Puffer beziehen.

Durch das Erstellen einer flachen Kopie eines konstanten oder unveränderlichen Werts wird die äußere Schicht der Unveränderlichkeit entfernt: Das Kopieren einer unveränderlichen Zeichenfolge ( immutable(char[])) gibt eine Zeichenfolge ( immutable(char)[]) zurück. Der unveränderliche Zeiger und die Länge werden kopiert und die Kopien sind veränderbar. Die referenzierten Daten wurden nicht kopiert und behalten ihren Qualifier, im Beispiel immutable. Es kann entfernt werden, indem man eine tiefere Kopie anfertigt, zB mit der dupFunktion.

Java

Ein klassisches Beispiel für ein unveränderliches Objekt ist eine Instanz der Java- StringKlasse

String s = "ABC";
s.toLowerCase();

Die Methode toLowerCase()ändert die enthaltenen Daten "ABC" nicht s. Stattdessen wird ein neues String-Objekt instanziiert und während seiner Konstruktion mit den Daten "abc" versehen. Eine Referenz auf dieses String-Objekt wird von der toLowerCase()Methode zurückgegeben. Damit der String sdie Daten "abc" enthält, ist ein anderer Ansatz erforderlich:

s = s.toLowerCase();

Jetzt sverweist der String auf ein neues String-Objekt, das "abc" enthält. Es gibt nichts in der Syntax der Deklaration der Klasse String, die sie als unveränderlich erzwingt; Stattdessen beeinflusst keine der Methoden der String-Klasse jemals die Daten, die ein String-Objekt enthält, wodurch es unveränderlich wird.

Das Schlüsselwort final( ausführlicher Artikel ) wird verwendet, um unveränderliche primitive Typen und Objektreferenzen zu implementieren, aber es kann die Objekte selbst nicht unveränderlich machen. Siehe unten Beispiele:

Variablen vom primitiven Typ ( int, long, short, etc.) können nach der Definition neu zugewiesen werden. Dies kann durch die Verwendung von verhindert werden final.

int i = 42; //int is a primitive type
i = 43; // OK

final int j = 42;
j = 43; // does not compile. j is final so can't be reassigned

Referenztypen können nicht nur mit dem finalSchlüsselwort unveränderlich gemacht werden . finalverhindert nur eine Neuzuordnung.

final MyObject m = new MyObject(); //m is of reference type
m.data = 100; // OK. We can change state of object m (m is mutable and final doesn't change this fact)
m = new MyObject(); // does not compile. m is final so can't be reassigned

Primitive Wrapper ( Integer, Long, Short, Double, Float, Character, Byte, Boolean) sind ebenfalls alle unveränderlich. Unveränderliche Klassen können implementiert werden, indem einige einfache Richtlinien befolgt werden.

JavaScript

In JavaScript sind alle primitiven Typen (Undefined, Null, Boolean, Number, BigInt, String, Symbol) unveränderlich, aber benutzerdefinierte Objekte sind im Allgemeinen veränderbar.

function doSomething(x) { /* does changing x here change the original? */ };
var str = 'a string';
var obj = { an: 'object' };
doSomething(str);         // strings, numbers and bool types are immutable, function gets a copy
doSomething(obj);         // objects are passed in by reference and are mutable inside function
doAnotherThing(str, obj); // `str` has not changed, but `obj` may have.

Um die Unveränderlichkeit in einem Objekt zu simulieren, kann man Eigenschaften als schreibgeschützt (beschreibbar: false) definieren.

var obj = {};
Object.defineProperty(obj, 'foo', { value: 'bar', writable: false });
obj.foo = 'bar2'; // silently ignored

Der obige Ansatz ermöglicht jedoch immer noch das Hinzufügen neuer Eigenschaften. Alternativ kann man Object.freeze verwenden , um vorhandene Objekte unveränderlich zu machen.

var obj = { foo: 'bar' };
Object.freeze(obj);
obj.foo = 'bars'; // cannot edit property, silently ignored
obj.foo2 = 'bar2'; // cannot add property, silently ignored

Mit der Implementierung von ECMA262 kann JavaScript unveränderliche Referenzen erstellen, die nicht neu zugewiesen werden können. Die Verwendung einer constDeklaration bedeutet jedoch nicht, dass der Wert der schreibgeschützten Referenz unveränderlich ist, sondern dass der Name keinem neuen Wert zugewiesen werden kann.

const ALWAYS_IMMUTABLE = true;

try {
  ALWAYS_IMMUTABLE = false;
} catch (err) {
  console.log("Can't reassign an immutable reference.");
}

const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]

Die Verwendung von unveränderlichen Zuständen hat sich in JavaScript seit der Einführung von React zu einem steigenden Trend entwickelt , das Flux-ähnliche Zustandsverwaltungsmuster wie Redux bevorzugt .

Perl

In Perl kann man mit der Moo-Bibliothek eine unveränderliche Klasse erstellen, indem man einfach alle Attribute schreibgeschützt deklariert:

package Immutable;
use Moo;

has value => (
    is      => 'ro',   # read only
    default => 'data', # can be overridden by supplying the constructor with
                       # a value: Immutable->new(value => 'something else');
);

1;

Das Erstellen einer unveränderlichen Klasse erforderte früher zwei Schritte: erstens das Erstellen von Accessoren (entweder automatisch oder manuell), die die Änderung von Objektattributen verhindern, und zweitens das direkte Ändern der Instanzdaten von Instanzen dieser Klasse (dies wurde normalerweise in einem Hash gespeichert). Referenz und könnte mit der lock_hash-Funktion von Hash::Util gesperrt werden):

package Immutable;
use strict;
use warnings;
use base qw(Class::Accessor);
# create read-only accessors
__PACKAGE__->mk_ro_accessors(qw(value));
use Hash::Util 'lock_hash';

sub new {
    my $class = shift;
    return $class if ref($class);
    die "Arguments to new must be key => value pairs\n"
        unless (@_ % 2 == 0);
    my %defaults = (
        value => 'data',
    );
    my $obj = {
        %defaults,
        @_,
    };
    bless $obj, $class;
    # prevent modification of the object data
    lock_hash %$obj;
}
1;

Oder mit einem manuell geschriebenen Accessor:

package Immutable;
use strict;
use warnings;
use Hash::Util 'lock_hash';

sub new {
    my $class = shift;
    return $class if ref($class);
    die "Arguments to new must be key => value pairs\n"
        unless (@_ % 2 == 0);
    my %defaults = (
        value => 'data',
    );
    my $obj = {
        %defaults,
        @_,
    };
    bless $obj, $class;
    # prevent modification of the object data
    lock_hash %$obj;
}

# read-only accessor
sub value {
    my $self = shift;
    if (my $new_value = shift) {
        # trying to set a new value
        die "This object cannot be modified\n";
    } else {
        return $self->{value}
    }
}
1;

Python

In Python sind einige integrierte Typen (Zahlen, Boolesche Werte, Zeichenfolgen, Tupel, Frozensets) unveränderlich, aber benutzerdefinierte Klassen sind im Allgemeinen veränderbar. Um die Unveränderlichkeit in einer Klasse zu simulieren, könnte man das Setzen und Löschen von Attributen überschreiben, um Ausnahmen auszulösen:

class ImmutablePoint:
    """An immutable class with two attributes 'x' and 'y'."""

    __slots__ = ['x', 'y']

    def __setattr__(self, *args):
        raise TypeError("Can not modify immutable instance.")

    __delattr__ = __setattr__

    def __init__(self, x, y):
        # We can no longer use self.value = value to store the instance data
        # so we must explicitly call the superclass
        super().__setattr__('x', x)
        super().__setattr__('y', y)

Die standardmäßigen Bibliothekshelfer collections.namedtupleund typing.NamedTuple, verfügbar ab Python 3.6, erstellen einfache unveränderliche Klassen. Das folgende Beispiel entspricht ungefähr dem obigen, plus einige tupelartige Funktionen:

from typing import NamedTuple
import collections

Point = collections.namedtuple('Point', ['x', 'y'])

# the following creates a similar namedtuple to the above
class Point(NamedTuple):
    x: int
    y: int

Eingeführt in Python 3.7, dataclassesermöglicht es Entwicklern, Unveränderlichkeit mit eingefrorenen Instanzen zu emulieren . Wenn eine eingefrorene Datenklasse erstellt wird, dataclasseswird sie überschrieben __setattr__()und ausgelöst __delattr__(), FrozenInstanceErrorwenn sie aufgerufen wird.

from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: int
    y: int

Schläger

Racket weicht wesentlich von anderen Schema- Implementierungen ab, indem es seinen Kernpaartyp ("Kons-Zellen") unveränderlich macht. Stattdessen bietet es einen parallelen veränderlichen Paartyp, via mcons, mcar, set-mcar!usw. Darüber hinaus werden viele unveränderliche Typen unterstützt, zum Beispiel unveränderliche Zeichenfolgen und Vektoren, und diese werden ausgiebig verwendet. Neue Strukturen sind standardmäßig unveränderlich, es sei denn, ein Feld wurde ausdrücklich als veränderbar deklariert, oder die gesamte Struktur:

(struct foo1 (x y))             ; all fields immutable
(struct foo2 (x [y #:mutable])) ; one mutable field
(struct foo3 (x y) #:mutable)   ; all fields mutable

Die Sprache unterstützt auch unveränderliche Hash-Tabellen, die funktional implementiert sind, und unveränderliche Wörterbücher.

Rost

Das Eigentumssystem von Rust ermöglicht es Entwicklern, unveränderliche Variablen zu deklarieren und unveränderliche Referenzen zu übergeben. Standardmäßig sind alle Variablen und Referenzen unveränderlich. Veränderliche Variablen und Referenzen werden explizit mit dem mutSchlüsselwort erstellt.

Konstante Gegenstände in Rust sind immer unveränderlich.

// constant items are always immutable
const ALWAYS_IMMUTABLE: bool = true;

struct Object {
    x: usize,
    y: usize,
}

fn main() {
    // explicitly declare a mutable variable
    let mut mutable_obj = Object { x: 1, y: 2 };
    mutable_obj.x = 3; // okay

    let mutable_ref = &mut mutable_obj;
    mutable_ref.x = 1; // okay

    let immutable_ref = &mutable_obj;
    immutable_ref.x = 3; // error E0594

    // by default, variables are immutable
    let immutable_obj = Object { x: 4, y: 5 };
    immutable_obj.x = 6; // error E0596

    let mutable_ref2 = 
        &mut immutable_obj; // error E0596

    let immutable_ref2 = &immutable_obj;
    immutable_ref2.x = 6; // error E0594
    
}

Scala

In Scala kann jede Entität (engen eine Bindung) als veränderlich oder unveränderlich definiert werden: In der Deklaration kann man val(Wert) für unveränderliche Entitäten und var(Variable) für veränderliche Entitäten verwenden. Beachten Sie, dass eine unveränderliche Bindung, auch wenn sie nicht neu zugewiesen werden kann, auf ein veränderliches Objekt verweisen kann und es immer noch möglich ist, mutierende Methoden für dieses Objekt aufzurufen: Die Bindung ist unveränderlich, aber das zugrunde liegende Objekt kann veränderlich sein.

Zum Beispiel das folgende Code-Snippet:

val maxValue = 100
var currentValue = 1

definiert eine unveränderliche Entität maxValue(der Integer-Typ wird zur Kompilierzeit abgeleitet) und eine veränderliche Entität namens currentValue.

Standardmäßig sind Sammlungsklassen wie Listund Mapunveränderlich, daher geben update-methods eine neue Instanz zurück, anstatt eine vorhandene zu mutieren. Dies mag ineffizient klingen, aber die Implementierung dieser Klassen und ihre Garantien für die Unveränderlichkeit bedeuten, dass die neue Instanz vorhandene Knoten wiederverwenden kann, was insbesondere bei der Erstellung von Kopien sehr effizient ist.

Siehe auch

Verweise

Dieser Artikel enthält Material aus dem Perl Design Patterns Book

Externe Links