Vec<T> oder Arc<[T]>
Arc<[T]> anstelle von Vec<T> (bzw. Arc<str> statt String) verwenden bei unveränderlichen (immutable) Daten.
Verwendung
Abschnitt betitelt „Verwendung“Da Arc<[T]> das Trait Deref<Target=[T]> implementiert, können alle Lese-Methoden, die man von Vektoren kennt (z. B. iterieren, indizieren, Länge abfragen), genauso verwendet werden. Es ist oft ein “Drop-in Replacement”.
use std::sync::Arc;
fn main() { let daten_vec = [10, 20, 30, 40]; let shared_daten: Arc<[i32]> = Arc::from(daten_vec);
println!("Länge: {}", shared_daten.len()); println!("Erstes Element: {}", shared_daten[0]);
for zahl in shared_daten.iter() { println!("Zahl: {}", zahl); }}Hauptvorteile
Abschnitt betitelt „Hauptvorteile“Extrem günstiges Klonen
Abschnitt betitelt „Extrem günstiges Klonen“Das Klonen eines Arc passiert in konstanter Zeit (O(1)). Es wird lediglich ein Referenzzähler erhöht, statt wie bei Vec den gesamten Speicherinhalt zu kopieren und neu zu allozieren.
use std::sync::Arc;
fn main() { let a: Arc<[i32]> = Arc::from([1, 2, 3, 4, 5]);
// Klonen ist hier sehr günstig (nur Zähler-Update) let b = a.clone(); let c = a.clone();
// Die Daten liegen an der gleichen Adresse println!("Original Daten bei: {:p}", &a[0]); println!("Kopie1 Daten bei: {:p}", &c[0]);}Geringerer Speicherverbrauch auf dem Stack
Abschnitt betitelt „Geringerer Speicherverbrauch auf dem Stack“Ein Arc<[T]> ist nur 16 Bytes groß (Pointer + Länge), während ein Vec<T> 24 Bytes benötigt (Pointer + Länge + Kapazität). Bei vielen Objekten summiert sich dieser Unterschied.
use std::mem::size_of;use std::sync::Arc;
fn main() { // 24 Bytes (Pointer + Länge + Kapazität) println!("Größe von Vec<i32>: {} Bytes", size_of::<Vec<i32>>());
// 16 Bytes (Pointer + Länge) println!("Größe von Arc<[i32]>: {} Bytes", size_of::<Arc<[i32]>>());}Wann man Arc<[T]> statt Vec nutzen sollte
Abschnitt betitelt „Wann man Arc<[T]> statt Vec nutzen sollte“- Unveränderliche Daten: Wenn du Datenstrukturen aufbaust, die einmal erstellt und danach nicht mehr verändert (mutiert), sondern nur noch gelesen oder herumgereicht werden.
- Häufiges Klonen: Arc ist besonders stark, wenn Daten oft geklont werden müssen.
Textdaten: Arc vs. String
Abschnitt betitelt „Textdaten: Arc vs. String“Die gleichen Argumente gelten für Strings. Man sollte Arc<str> für unveränderliche Texte verwenden.
Wichtig: Vermeide Arc<String>. Dies führt zu einer doppelten Indirektion (Zeiger auf Arc -> Zeiger auf String -> Zeiger auf Daten), was ineffizient und schlecht für die Performance ist.
use std::sync::Arc;
fn main() { // Stack -> Heap(Arc) -> Heap(Buchstaben) let a: Arc<str> = Arc::from("Hallo"); println!("Inhalt: {a}");
// Stack -> Heap(Arc) -> Heap(String-Struct) -> Heap(Buchstaben) let ineffizient: Arc<String> = Arc::new(String::from("Bitte vermeiden"));
// 16 Bytes println!( "Größe Arc<str>: {} Bytes", std::mem::size_of_val(&a) );}Alternative: Box<[T]>
Abschnitt betitelt „Alternative: Box<[T]>“Box<[T]> ist eine Alternative, wenn Daten nicht geclont werden. Box ist noch effizienter als Arc, da es keinen Overhead für den Referenzzähler hat.
fn main() { // Kein Rc-Overhead (unveränderlich, nur ein Besitzer) let a: Box<[i32]> = Box::new([1, 2, 3]);
// 16 Bytes println!( "Größe Box<[i32]>: {} Bytes", std::mem::size_of_val(&a) );}