Quelles sont les différences entre `String` et `str ' de Rust?
Pourquoi ne Rouille 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 l'un d'eux se soit obsolète?
6 réponses
String
est le type de chaîne de tas dynamique, comme Vec
: Utilisez-le lorsque vous devez posséder ou modifier vos données de chaîne.
str
est immuable1 séquence d'octets UTF-8 de longueur dynamiques quelque part dans la mémoire. Puisque la taille est inconnue, on ne peut gérer que derrière un pointeur. Cela signifie que str
le plus souvent2 apparaît comme &str
: une référence à certaines données UTF-8, normalement appelées "tranche de chaîne" ou simplement "tranche". une tranche est juste une vue sur 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 lorsque le programme s'exécute. - dans un tas alloué
String
:String
déréférences à une vue&str
des données deString
. -
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 en tant que
&str
:use std::str; let x: &[u8] = &[b'a', b'b', b'c']; let stack_str: &str = str::from_utf8(x).unwrap();
Dans résumé, utilisez String
si vous avez besoin de données de chaîne possédées (comme passer des chaînes à d'autres tâches, ou les construire à l'exécution), et utilisez &str
Si vous avez seulement besoin d'une vue d'une chaîne.
Ceci est identique à la relation entre un vecteur Vec<T>
et une tranche &[T]
, et est semblable à la relation entre la valeur T
et par référence &T
pour les types généraux.
1 un str
est de longueur fixe; vous ne pouvez pas écrire d'octets au-delà de la fin ou laisser des octets non valides. Puisque UTF-8 est un codage à largeur variable, cela force efficacement tous les str
s à être immuables. En général, la mutation nécessite d'écrire plus ou moins d'octets qu'auparavant (par exemple, remplacer un a
(1 octet) par un ä
(2 + octets) nécessiterait de faire plus de place dans le str
).
2 pour le moment, il ne peut que apparaître comme &str
, mais les types de taille dynamique peuvent permettre des choses comme Rc<str>
pour une séquence D'octets UTF-8 comptés par référence. Il peut également ne pas, str
ne correspond pas parfaitement au schéma DST, car il n'y a pas encore de version de taille fixe.
J'ai un fond C++ et j'ai trouvé très utile de penser à String
et &str
en termes C++:
- une rouille {[2] } est comme un
std::string
; elle possède la mémoire et fait le sale travail de gestion de la mémoire. - Rouille
&str
, c'est comme unchar*
(mais un peu plus sophistiqué); il nous montre le début d'un morceau de la même façon vous pouvez obtenir un pointeur vers le contenu destd::string
.
L'un d'eux va-t-il disparaître? Je ne le crois pas. Elles servent à deux objectifs:
String
conserve le tampon et est très pratique à utiliser. &str
est léger et devrait être utilisé pour "regarder" dans les cordes. Vous pouvez rechercher, diviser, analyser et même remplacer des morceaux sans avoir besoin d'allouer de nouvelle mémoire.
&str
pouvez regarder à l'intérieur d'un String
comme il peut pointer vers une chaîne littérale. 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 (lecture seule bien que)
let a: &str = "hello rust";
str
, seulement utilisé comme &str
, est une tranche de chaîne, une référence à un tableau d'octets UTF-8.
String
est ce qui était ~str
, Un tableau d'octets UTF-8 cultivable et possédé.
Ils sont en fait complètement différents. Tout d'abord, un str
n'est rien d'autre qu'une chose au niveau du type; il ne peut être raisonné qu'au niveau du type car c'est un type dit de taille dynamique (DST). La taille que prend 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 au moment de la compilation Quelle est la taille de chaque variable. Un str
est conceptuellement juste une ligne de u8
octets, avec la garantie qu'il formulaires UTF-8 valides. Quelle est la taille de la rangée? Personne ne sait jusqu'à l'exécution, donc il ne peut pas être stocké dans une variable.
La chose intéressante est qu'un &str
ou tout autre pointeur vers un str
comme Box<str>
existe-t-il au moment de l'exécution. C'est un soi-disant "pointeur fat"; c'est un pointeur avec des informations supplémentaires (dans ce cas la taille de la chose qu'il pointe) donc c'est deux fois plus grand. En fait, un &str
est assez proche d'un String
(mais pas à un &String
). Un &str
est deux mots; un pointeur à l' premier octet d'un str
et un autre nombre qui décrit combien d'octets de long le str
est.
, Contrairement à ce qui est dit, un 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 sûres qui le mutent garantissent que la contrainte UTF-8 est maintenue car si cela est violé, nous avons un comportement indéfini car la bibliothèque suppose que cette contrainte est vraie et ne la vérifie pas.
Alors qu'est-ce qui est 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é de la str
tampon sur le tas, toujours sur le tas (un str
n'est pas nécessairement sur le tas), il gère avant d'être rempli et doit ré-allouer. le String
fondamentalement possède un str
, comme ils disent; il contrôle et pouvez la redimensionner et la réaffecter elle lorsqu'elle le juge opportun. Donc un String
est comme dit plus proche d'un &str
que de str
.
Une autre chose est un Box<str>
; cela aussi possède un str
et sa représentation d'exécution est la même qu'un &str
mais il possède également le str
contrairement au &str
mais il ne peut pas le redimensionner car il ne connaît pas sa capacité donc fondamentalement 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 voulez 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'elle peut contenir n'importe quel type dont la taille n'est pas dynamique.
Le l'utilisation de str
au niveau du type consiste principalement à créer des abstractions génériques avec &str
; Il existe au niveau du type pour pouvoir écrire commodément des traits. En théorie str
comme une chose de type n'avait pas besoin d'exister et seulement &str
mais cela signifierait que beaucoup de code supplémentaire devrait être écrit qui peut maintenant être générique.
&str
c'est super utile de pouvoir avoir plusieurs sous-chaînes d'une String
sans avoir à copier; comme l'a dit un String
propriétaire de le str
sur le tas qu'il gère et si vous ne pouviez créer qu'une sous-chaîne d'un String
avec un nouveau String
, Il faudrait le 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 dé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 str
de la même chaîne. string
est celui qui possède le tampon str
complet réel sur le tas et les sous-chaînes &str
ne sont que des pointeurs gras vers ce tampon sur le tas.
En termes simples, String
est le type de données stocké sur le tas comme Vec
et vous avez accès au pointeur vers cet emplacement.
&str
est un type de tranche. Cela signifie que c'est juste une référence à String
déjà présent quelque part dans le tas.
&str
ne fait aucune allocation à l'exécution. Donc, pour des raisons de mémoire, vous pouvez utiliser &str
sur String
. Mais, gardez à l'esprit que lorsque vous utilisez &str
, vous devrez peut-être faire face à des durées de vie explicites.
Chaîne sont un vecteur de char, vous pouvez y accéder et modifier str sont immuables