Warum hat Rust sowohl String
als auch str
? Was sind die Unterschiede zwischen ihnen und wann sollte man eine gegenüber der anderen verwenden? Wird eine von ihnen veraltet?
Antworten
Zu viele Anzeigen?std::String
ist einfach ein Vektor von u8
. Du kannst die Definition in Quellcode finden. Es ist heap-allociert und erweiterbar.
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec,
}
str
ist ein primitiver Typ, auch genannt string slice. Ein String-Slice hat eine feste Größe. Ein Literal-String wie let test = "hello world"
hat den Typ 'static str
. test
ist eine Referenz zu diesem statisch allokierten String. &str
kann nicht verändert werden, zum Beispiel,
let mut word = "hello world";
word[0] = 's';
word.push('\n');
str
hat jedoch einen veränderbaren Slice &mut str
, zum Beispiel: pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
let mut s = "Per Martin-Löf".to_string();
{
let (first, last) = s.split_at_mut(3);
first.make_ascii_uppercase();
assert_eq!("PER", first);
assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);
Aber eine kleine Änderung an UTF-8 kann die Byte-Länge ändern und ein Slice kann nicht auf sein Referenzobjekt umstellen.
In einfachen Worten ist String
ein Datentyp, der im Heap gespeichert ist (genauso wie Vec
), und du hast Zugriff auf diesen Speicherort.
&str
ist ein Slicetyp. Das bedeutet, es handelt sich nur um einen Verweis auf einen bereits vorhandenen String
irgendwo im Heap.
&str
führt keine Speicherzuweisung zur Laufzeit durch. Aus Gründen des Speichers kannst du also &str
anstelle von String
verwenden. Beachte jedoch, dass du bei der Verwendung von &str
eventuell mit expliziten Lebensdauern umgehen musst.
In diesen 3 verschiedenen Typen
let noodles = "noodles".to_string(); let oodles = &noodles[1..]; let poodles = "_"; // dies ist ein String-Literal
Ein String hat einen änderbaren Puffer, der UTF-8-Text enthält. Der Puffer wird im Heap allokiert, sodass er seinen Puffer bei Bedarf oder auf Anforderung ändern kann. Im Beispiel ist "noodles" ein String, der einen acht Byte großen Puffer besitzt, von dem sieben Bytes genutzt werden. Man kann sich einen String als einen Vec vorstellen, der garantiert wohlgeformten UTF-8-Text enthält; tatsächlich ist dies die Implementierung von
String
.Ein
&str
ist eine Referenz auf einen Lauf von UTF-8-Text, der von jemand anderem besessen wird: er "leiht" den Text. Im Beispiel ist oodles ein &str, der auf die letzten sechs Bytes des Textes von "noodles" verweist, und repräsentiert somit den Text "oodles." Wie andere Slice-Referenzen ist ein&str
einfat pointer
, der sowohl die Adresse der tatsächlichen Daten als auch deren Länge enthält. Man kann sich einen&str
einfach nur als einen &[u8] vorstellen, der garantiert wohlgeformten UTF-8-Text enthält.Ein
Stringliteral
ist ein&str
, der auf vorab allokierten Text verweist, der typischerweise im Nur-Lese-Speicher zusammen mit dem Maschinencode des Programms gespeichert ist. Im obenstehenden Beispiel ist poodles ein Stringliteral, das auf sieben Bytes zeigt, die beim Programmaufruf erstellt werden und bis zum Beenden des Programms erhalten bleiben.So werden sie im Speicher gespeichert
Referenz: Programming Rust, von Jim Blandy, Jason Orendorff, Leonora F. S. Tindall
Einige Anwendungen
beispiel_1.rs
fn main(){
let hello = String::("hello");
let any_char = hello[0];//Fehler
}
beispiel_2.rs
fn main(){
let hello = String::("hello");
for c in hello.chars() {
println!("{}",c);
}
}
beispiel_3.rs
fn main(){
let hello = String::("Strings sind cool");
let any_char = &hello[5..6]; // = let any_char: &str = &hello[5..6];
println!("{:?}",any_char);
}
Shadowing
fn main() {
let s: &str = "hallo"; // &str
let s: String = s.to_uppercase(); // String
println!("{}", s) // HALLO
}
Funktion
fn say_hello(to_whom: &str) { //Typumwandlung
println!("Hey {}!", to_whom)
}
fn main(){
let string_slice: &'static str = "du";
let string: String = string_slice.into(); // &str => String
say_hello(string_slice);
say_hello(&string);// &String
}
Concat
// String befindet sich auf dem Heap und kann in seiner Größe erhöht oder verkleinert werden
// Die Größe von &str ist festgelegt.
fn main(){
let a = "Foo";
let b = "Bar";
let c = a + b; //Fehler
// let c = a.to_string + b;
}
Beachten Sie, dass String
und &str
unterschiedliche Typen sind und dass Sie sich in 99% der Fälle nur um &str
kümmern sollten.