Quali sono le differenze tra Rust's `String` e` str`?


421

Perché Rust ha Stringe str? Quali sono le differenze tra Stringe str? Quando si usa Stringinvece di stre viceversa? Uno di loro viene deprecato?

Risposte:


491

Stringè il tipo di stringa heap dinamico, ad esempio Vec: utilizzalo quando devi possedere o modificare i dati della stringa.

strè una sequenza immutabile di 1 byte UTF-8 di lunghezza dinamica da qualche parte nella memoria. Poiché la dimensione è sconosciuta, è possibile gestirla solo dietro un puntatore. Ciò significa che il strpiù delle volte 2 appare come &str: un riferimento ad alcuni dati UTF-8, normalmente chiamati "slice slice" o semplicemente "slice". Una sezione è solo una vista su alcuni dati e tali dati possono essere ovunque, ad es

  • Nella memoria statica : una stringa letterale "foo"è a &'static str. I dati vengono hardcoded nell'eseguibile e caricati in memoria all'avvio del programma.
  • All'interno di un heap allocatoString : Stringdereferenze a una &strvista dei Stringdati.
  • Sullo stack : ad esempio, quanto segue crea un array di byte allocato nello stack e quindi ottiene una visualizzazione di tali dati come&str :

    use std::str;
    
    let x: &[u8] = &[b'a', b'b', b'c'];
    let stack_str: &str = str::from_utf8(x).unwrap();
    

In sintesi, utilizzare Stringse sono necessari dati di stringa di proprietà (come passare stringhe ad altri thread o crearli in fase di esecuzione) e utilizzare &strse è necessaria solo la visualizzazione di una stringa.

Questo è identico alla relazione tra un vettore Vec<T>e una sezione &[T]ed è simile alla relazione tra valore per Te riferimento &Tper i tipi generali.


1 A strè a lunghezza fissa; non è possibile scrivere byte oltre la fine o lasciare byte finali non validi. Poiché UTF-8 è una codifica a larghezza variabile, ciò costringe effettivamente tutti gli strs a essere immutabili in molti casi. In generale, la mutazione richiede la scrittura di un numero maggiore o minore di byte rispetto a prima (ad esempio, la sostituzione di un a(1 byte) con un ä(2+ byte) richiederebbe di creare più spazio nel str). Ci sono metodi specifici che possono modificare un &strposto, soprattutto quelli che gestiscono solo caratteri ASCII, come make_ascii_uppercase.

2 I tipi di dimensioni dinamiche consentono cose come Rc<str>per una sequenza di byte UTF-8 contati di riferimento da Rust 1.2. Rust 1.21 consente di creare facilmente questi tipi.


10
"sequenza di byte UTF-8 ( di lunghezza sconosciuta )" - non è aggiornato? I documenti dicono che "A &strè composto da due componenti: un puntatore ad alcuni byte e una lunghezza".
mrec,

11
Non è fuori moda (quella rappresentazione è stata abbastanza stabile), solo un po 'impreciso: non si sa in modo statico, a differenza, ad esempio, [u8; N].
huon,

2
@mrec non è noto al momento della compilazione, non è possibile formulare ipotesi sulla sua dimensione, ad esempio durante la creazione di uno stack frame. Ecco perché viene spesso trattato come un riferimento, che un riferimento è una dimensione nota al momento della compilazione, che è la dimensione di un puntatore.
Sekhat,

1
Aggiornamento: Rc<str>e Arc<str>ora sono utilizzabili tramite la libreria standard.
Centril

1
@cjohansson Gli oggetti allocati staticamente normalmente non vengono memorizzati né nell'heap né nello stack, ma nella propria area di memoria.
Brennan Vincent,

97

Ho un background C ++ e ho trovato molto utile pensare Stringe &strin termini C ++:

  • Una ruggine Stringè come una std::string; possiede la memoria e fa il lavoro sporco di gestione della memoria.
  • Una ruggine &strè come una char*(ma un po 'più sofisticata); ci indica l'inizio di un blocco nello stesso modo in cui è possibile ottenere un puntatore al contenuto di std::string.

Uno di loro sparirà? Non la penso così. Servono a due scopi:

Stringmantiene il buffer ed è molto pratico da usare. &strè leggero e dovrebbe essere usato per "guardare" nelle stringhe. È possibile cercare, dividere, analizzare e persino sostituire i blocchi senza dover allocare nuova memoria.

&strpuò guardare all'interno di a Stringin quanto può indicare una stringa letterale. Il codice seguente deve copiare la stringa letterale nella Stringmemoria gestita:

let a: String = "hello rust".into();

Il seguente codice ti consente di usare il valore letterale stesso senza copia (leggi solo però)

let a: &str = "hello rust";

13
come una stringa_view?
Abhinav Gauniyal,

2
Sì, come string_view ma intrinseco alla lingua e correttamente preso in prestito controllato.
Locka,

41

str, usato solo come &str, è una porzione di stringa, un riferimento a una matrice di byte UTF-8.

Stringè quello che era ~strun array di byte UTF-8 di proprietà coltivabile.


Tecnicamente, quello che era ~stradesso èBox<str>
jv110,

3
@ jv110: no, perché ~strera coltivabile mentre Box<str>non è coltivabile. (Quello ~stred ~[T]erano magicamente coltivabili, a differenza di qualsiasi altro oggetto ~, era esattamente il motivo Stringe Vec<T>furono introdotti, in modo che le regole fossero tutte semplici e coerenti.)
Chris Morgan,

18

In realtà sono completamente diversi. Prima di tutto, a strnon è altro che una cosa a livello di tipo; può essere ragionato solo a livello di tipo perché è un cosiddetto tipo di dimensioni dinamiche (DST). La dimensione che stroccupa non può essere conosciuta al momento della compilazione e dipende dalle informazioni di runtime - non può essere memorizzata in una variabile perché il compilatore deve sapere in fase di compilazione quale sia la dimensione di ciascuna variabile. A strè concettualmente solo una riga di u8byte con la garanzia che forma UTF-8 valido. Quanto è grande la fila? Nessuno lo sa fino al runtime, quindi non può essere archiviato in una variabile.

La cosa interessante è che una &stro qualsiasi altro puntatore a una strcome Box<str> fa esistere in fase di esecuzione. Questo è un cosiddetto "puntatore grasso"; è un puntatore con informazioni extra (in questo caso la dimensione dell'oggetto a cui punta), quindi è due volte più grande. In effetti, a &strè abbastanza vicino a a String(ma non a a &String). A &strè due parole; un puntatore a un primo byte di un stre un altro numero che descrive quanti byte sono lunghi str.

Contrariamente a quanto detto, a strnon deve essere immutabile. Se riesci a ottenere un &mut strpuntatore esclusivo a str, puoi mutarlo e tutte le funzioni sicure che lo mutano garantiscono che il vincolo UTF-8 sia rispettato perché se viene violato abbiamo un comportamento indefinito poiché la biblioteca presume che questo vincolo sia vero e non lo controlla.

Quindi cos'è un String? Sono tre parole; due sono uguali a &strma aggiunge una terza parola che è la capacità del strbuffer sull'heap, sempre sull'heap (a strnon è necessariamente sull'heap) che gestisce prima di essere riempito e deve riassegnare. il Stringfondo possiede un strcome si dice; lo controlla e può ridimensionarlo e riallocarlo quando lo ritiene opportuno. Quindi a Stringè come detto più vicino a &strche a str.

Un'altra cosa è a Box<str>; anche questo possiede un stre la sua rappresentazione di runtime è la stessa di un &strma possiede anche il strcontrario del &strma non può ridimensionarlo perché non conosce la sua capacità, quindi sostanzialmente Box<str>può essere visto come una lunghezza fissa Stringche non può essere ridimensionata (puoi convertilo sempre in a Stringse vuoi ridimensionarlo).

Esiste una relazione molto simile tra [T]e Vec<T>tranne che non esiste alcun vincolo UTF-8 e può contenere qualsiasi tipo le cui dimensioni non sono dinamiche.

L'uso stra livello di tipo serve principalmente a creare astrazioni generiche con &str; esiste a livello di tipo per poter scrivere comodamente i tratti. In teoria str, una cosa tipo non aveva bisogno di esistere e solo, &strma ciò avrebbe significato scrivere un sacco di codice extra che ora può essere generico.

&strè super utile per poter avere più sottostringhe diverse di a Stringsenza dover copiare; come detto a String possiede l' strheap che gestisce e se si potesse creare solo una sottostringa di a Stringcon una nuova String, sarebbe necessario copiarla perché tutto in Rust può avere un solo proprietario per gestire la sicurezza della memoria. Quindi, ad esempio, puoi tagliare una stringa:

let string: String   = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];

Abbiamo due sottostringhe diverse strdella stessa stringa. stringè quello che possiede il strbuffer completo effettivo sull'heap e le &strsottostringhe sono solo puntatori di grasso a quel buffer sull'heap.


4

std::Stringè semplicemente un vettore di u8. Puoi trovarne la definizione nel codice sorgente . È allocato in heap e coltivabile.

#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
    vec: Vec<u8>,
}

strè un tipo primitivo, chiamato anche stringa slice . Una sezione di stringa ha dimensioni fisse. Una stringa letterale come let test = "hello world"ha &'static strtipo. testè un riferimento a questa stringa allocata staticamente. &strnon può essere modificato, ad esempio,

let mut word = "hello world";
word[0] = 's';
word.push('\n');

strha una sezione mutabile &mut str, ad esempio: 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);

Ma una piccola modifica a UTF-8 può cambiare la sua lunghezza in byte e una sezione non può riallocare il suo referente.


0

In parole semplici, il Stringtipo di dati è memorizzato sull'heap (proprio come Vec) e hai accesso a quella posizione.

&strè un tipo di fetta. Ciò significa che è solo un riferimento a un già presente Stringda qualche parte nell'heap.

&strnon esegue alcuna allocazione in fase di esecuzione. Quindi, per ragioni di memoria, è possibile utilizzare &strsopra String. Tuttavia, tieni presente che durante l'utilizzo &strpotresti dover affrontare vite esplicite.


1
da qualche parte nel mucchio - questo non è completamente accurato.
Shepmaster

Quello che volevo dire era che strè viewgià presente Stringnell'heap.
00imvj00,

1
Capisco che intendevi questo, e sto dicendo che non è del tutto esatto. L '"heap" non è una parte obbligatoria dell'istruzione.
Shepmaster,

-1

Per persone C # e Java:

  • Ruggine ' String===StringBuilder
  • La &str stringa di Rust === (immutabile)

Mi piace pensare a &strcome una vista su una stringa, come una stringa internata in Java / C # dove non è possibile cambiarla, ma crearne una nuova.


1
La più grande differenza tra le stringhe Java / C # e le stringhe Rust è che Rust garantisce che la stringa sia unicode corretto, in quanto tale ottenere il terzo carattere in una stringa richiede più pensiero di un semplice "abc" [2]. (Dato che viviamo in un mondo multilingue, questa è una buona cosa.)
Scoiattolo

Questo non è corretto . Il tema della mutabilità è già affrontato nella risposta più votata; per favore leggilo per saperne di più.
Shepmaster,

-5

Ecco una spiegazione semplice e veloce.

String- Una struttura di dati allocabile heap allocabile e coltivabile. Può essere costretto a &str.

str- è (ora, man mano che Rust si evolve) stringa mutabile di lunghezza fissa che vive nell'heap o nel binario. È possibile interagire solo strcome tipo preso in prestito tramite una vista di tipo stringa, ad esempio &str.

Considerazioni sull'uso:

Preferisci Stringse vuoi possedere o mutare una stringa - come passare la stringa a un altro thread, ecc.

Preferisci &strse vuoi avere una vista di sola lettura di una stringa.


Questo non è corretto . Il tema della mutabilità è già affrontato nella risposta più votata; per favore leggilo per saperne di più.
Shepmaster,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.