web-dev-qa-db-fra.com

Quelles sont les différences entre `String` et` str` de Rust?

Pourquoi Rust a-t-il String et str? Quelles sont les différences entre String et str? Quand utilise-t-on String au lieu de str et vice versa? Est-ce que l'un d'eux devient obsolète?

245
Daniel Fath

String est le type de chaîne dynamique du tas, tel que Vec: utilisez-le lorsque vous devez posséder ou modifier vos données de chaîne.

str est un immuable1 séquence d'octets UTF-8 de longueur dynamique quelque part en mémoire. Comme la taille est inconnue, on ne peut la manipuler que derrière un pointeur. Cela signifie que str le plus souvent2 Apparaît sous la forme &str: référence à certaines données UTF-8, normalement appelée "tranche de chaîne" ou simplement "tranche". Une tranche est simplement une vue de certaines données, et ces données peuvent être n'importe où, par exemple.

  • Dans le stockage statique: un littéral de chaîne "foo" est un &'static str. Les données sont codées en dur dans l'exécutable et chargées en mémoire lors de l'exécution du programme.
  • À l'intérieur d'un segment alloué String: String DEREFERENCES À UNE VUE &str des données de la String.
  • Sur la pile: par exemple. Ce qui suit crée un tableau d'octets alloué à la pile, puis obtient une vue de ces données sous la forme &str :

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

En résumé, utilisez String si vous avez besoin de données de chaîne possédées (telles que le passage de chaînes à d'autres threads ou leur construction à l'exécution), et &str si vous avez uniquement besoin d'une vue d'une chaîne.

Ceci est identique à la relation entre un vecteur Vec<T> et une tranche &[T] et est similaire à la relation entre valeurT et référence&T pour les types généraux.


1 Une str est de longueur fixe; vous ne pouvez pas écrire d'octets au-delà de la fin ni laisser des octets non valides à la fin. UTF-8 étant un codage à largeur variable, cela force effectivement toutes les strs à rester immuables dans de nombreux cas. En général, une mutation nécessite d'écrire plus ou moins d'octets qu'auparavant (par exemple, remplacer une a (1 octet) par un ä (plus de 2 octets) nécessiterait de laisser plus de place à la str). Certaines méthodes spécifiques peuvent modifier un &str à la place, principalement celles qui gèrent uniquement les caractères ASCII, comme make_ascii_uppercase .

2 Les types de taille dynamique autorisent des choses comme Rc<str> pour une séquence de références comptées en octets UTF-8 depuis Rust 1.2. Rust 1.21 permet de créer facilement ces types. 

297
huon

J'ai un background C++ et j'ai trouvé très utile de penser à String et &str en termes C++:

  • Une Rust String est comme un std::string; il possède la mémoire et fait le sale boulot de la gestion de la mémoire.
  • Un Rust &str est comme un char* (mais un peu plus sophistiqué); cela nous indique le début d'un morceau de la même manière que vous pouvez obtenir un pointeur sur le contenu de std::string.

Est-ce que l'un d'eux va disparaître? Je ne pense pas. Ils servent deux objectifs:

String conserve le tampon et est très pratique à utiliser. &str est léger et doit être utilisé pour "regarder" dans les chaînes. Vous pouvez rechercher, scinder, analyser et même remplacer des morceaux sans avoir à allouer de la mémoire. 

&str peut regarder à l'intérieur d'une String car il peut pointer sur un littéral de chaîne. Le code suivant doit copier la chaîne littérale dans la mémoire gérée String:

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

Le code suivant vous permet d'utiliser le littéral lui-même sans copie (en lecture seule cependant)

let a: &str = "hello Rust";
45
Luis Ayuso

str , utilisé uniquement en tant que &str, est une tranche de chaîne, une référence à un tableau d'octets UTF-8.

String est ce qui était auparavant ~str, un tableau d'octets UTF-8 pouvant être développé et développé.

35
Chris Morgan

Ils sont en fait complètement différents. Tout d’abord, une str n’est rien d’autre qu’une chose de type; il ne peut être raisonné qu'au niveau du type car il s'agit d'un type dit de taille dynamique (DST). La taille occupée par str ne peut pas être connue au moment de la compilation et dépend des informations d'exécution. Elle ne peut pas être stockée dans une variable car le compilateur doit savoir à la compilation quelle est la taille de chaque variable. Une str n'est théoriquement qu'une rangée de u8 octets avec la garantie qu'elle constitue un UTF-8 valide. Quelle est la taille de la rangée? Personne ne le sait avant l'exécution, il ne peut donc pas être enregistré dans une variable.

La chose intéressante est qu'un &str ou tout autre pointeur sur une str comme Box<str> existe existe au moment de l'exécution. C'est ce qu'on appelle un "gros pointeur"; c'est un pointeur avec des informations supplémentaires (dans ce cas, la taille de la chose sur laquelle il pointe), il est donc deux fois plus grand. En fait, un &str est assez proche d'un String (mais pas d'un &String). Un &str est deux mots; un pointeur sur le premier octet d'un str et un autre nombre décrivant le nombre d'octets longs du str.

Contrairement à ce qui est dit, une str n'a pas besoin d'être immuable. Si vous pouvez obtenir un &mut str en tant que pointeur exclusif sur le str, vous pouvez le muter et toutes les fonctions de sécurité qui le miment garantissent que la contrainte UTF-8 est respectée, car elle est violée. ont un comportement indéfini car la bibliothèque suppose que cette contrainte est vraie et ne la vérifie pas.

Alors qu'est-ce qu'un String? C'est trois mots; deux sont les mêmes que pour &str mais il ajoute un troisième mot qui est la capacité du tampon str sur le tas, toujours sur le tas (un str n'est pas nécessairement sur le tas ) il gère avant qu'il soit rempli et doit être réalloué. la String possède fondamentalement possède a str comme on dit; il le contrôle et peut le redimensionner et le réaffecter quand bon lui semble. Ainsi, une String est plus proche d'un &str que d'un str.

Une autre chose est un Box<str>; ceci possède également un str et sa représentation d'exécution est identique à un &str mais il possède également le str contrairement au &str mais il ne peut pas le redimensionner car il ne le sait pas sa capacité, donc un Box<str> peut être considéré comme un String de longueur fixe qui ne peut pas être redimensionné (vous pouvez toujours le convertir en String si vous souhaitez le redimensionner).

Une relation très similaire existe entre [T] et Vec<T> sauf qu'il n'y a pas de contrainte UTF-8 et qu'il peut contenir tout type dont la taille n'est pas dynamique.

L'utilisation de str au niveau du type sert principalement à créer des abstractions génériques avec &str; il existe au niveau des types pour pouvoir écrire facilement des traits. En théorie, str en tant que chose type n'a pas besoin d'exister et seul &str mais cela signifierait qu'il faudrait écrire beaucoup de code supplémentaire qui peut maintenant être générique.

&str est super utile pour pouvoir avoir plusieurs sous-chaînes différentes d'un String sans avoir à copier; comme dit un String possède le str sur le tas qu'il gère et si vous pouviez seulement créer une sous-chaîne d'un String avec un nouveau String il aurait à copier car tout dans Rust ne peut avoir qu'un seul propriétaire pour gérer la sécurité de la mémoire. Ainsi, par exemple, vous pouvez couper une chaîne:

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

Nous avons deux sous-chaînes différentes strs de la même chaîne. string est celui qui possède le tampon complet str présent sur le tas et les sous-chaînes &str ne sont que de gros pointeurs vers ce tampon sur le tas.

3
Zorf

Pour les personnes C # et Java:

  • Rust 'String === StringBuilder
  • La chaîne &str === (immuable) de Rust

J'aime penser à &str comme à une vue sur une chaîne, comme une chaîne internée en Java/C # où vous ne pouvez pas la modifier, créez-en une nouvelle.

0
Squirrel

std::String est simplement un vecteur de u8. Vous pouvez trouver sa définition dans code source . C'est un tas alloué et qui peut être développé. 

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

str est un type primitif, également appelé string slice. Une tranche de chaîne a une taille fixe. Une chaîne littérale telle que let test = "hello world" a le type &'static str. test est une référence à cette chaîne allouée de manière statique. &str ne peut pas être modifié, par exemple,

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

str a une tranche mutable &mut str, par exemple: 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);

Mais une petite modification apportée à UTF-8 peut changer sa longueur en octets, et une tranche ne peut pas réaffecter son référent. 

0
Aperion

En termes simples, String est le type de données stocké sur le tas (tout comme Vec), et vous avez accès à cet emplacement.

&str est un type de tranche. Cela signifie qu'il fait simplement référence à une String déjà présente quelque part dans le tas. 

&str ne fait aucune allocation au moment de l'exécution. Donc, pour des raisons de mémoire, vous pouvez utiliser &str sur String. Cependant, gardez à l'esprit que lorsque vous utilisez &str, vous devrez peut-être gérer des durées de vie explicites.

0
00imvj00