Zum Inhalt springen

Vec<T> oder Arc<[T]>

Arc<[T]> anstelle von Vec<T> (bzw. Arc<str> statt String) verwenden bei unveränderlichen (immutable) Daten.

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);
}
}

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]);
}

Arc<[i32]>

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]>>());
}
  • 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.

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)
);
}

Arc<str>

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)
);
}

Box<[i32]>