Pourquoi Rust a String
- str
t-il et ? Quelles sont les différences entre String
et str
? Quand utilise-t-on à la String
place de str
et vice versa? L'un d'eux est-il devenu obsolète?
Pourquoi Rust a String
- str
t-il et ? Quelles sont les différences entre String
et str
? Quand utilise-t-on à la String
place de str
et vice versa? L'un d'eux est-il devenu obsolète?
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 une séquence immuable de 1 octet UTF-8 de longueur dynamique quelque part en mémoire. La taille étant inconnue, on ne peut la gérer que derrière un pointeur. Cela signifie que le str
plus souvent 2 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
"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.String
: String
déréférences à une &str
vue des String
données de.Sur la pile : par exemple, ce qui suit crée un tableau d'octets alloué par pile, puis obtient une vue de ces données sous forme de&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 détenues (comme passer des chaînes à d'autres threads ou les créer lors de l'exécution) et utilisez &str
si vous n'avez besoin que 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 par valeur T
et par référence &T
pour les types généraux.
1 A str
est de longueur fixe; vous ne pouvez pas écrire d'octets au-delà de la fin ou laisser des octets de fin non valides. Étant donné que UTF-8 est un codage à largeur variable, cela oblige effectivement tous les str
s à être immuables dans de nombreux cas. 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
). Il existe des méthodes spécifiques qui peuvent modifier un &str
en place, principalement celles qui ne gèrent que des caractères ASCII, comme make_ascii_uppercase
.
2 Les types de taille dynamique permettent des choses comme Rc<str>
une séquence d'octets UTF-8 comptés par référence depuis Rust 1.2. Rust 1.21 permet de créer facilement ces types.
[u8; N]
.
Rc<str>
et Arc<str>
sont désormais utilisables via la bibliothèque standard.
J'ai une formation en C ++ et j'ai trouvé très utile de penser String
et &str
en termes C ++:
String
est comme un std::string
; il possède la mémoire et fait le sale boulot de gérer la mémoire.&str
est comme un char*
(mais un peu plus sophistiqué); il 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 fins:
String
conserve le tampon et est très pratique à utiliser. &str
est léger et doit être utilisé pour "regarder" les chaînes. Vous pouvez rechercher, fractionner, analyser et même remplacer des morceaux sans avoir à allouer de nouvelle mémoire.
&str
peut regarder à l'intérieur d'un String
car il peut pointer vers un littéral de chaîne. Le code suivant doit copier la chaîne littérale dans la String
mémoire gérée:
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";
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 autrefois ~str
, un tableau d'octets UTF-8 évolutif et détenu.
~str
est maintenantBox<str>
~str
était possible de le développer alors qu'il Box<str>
ne l'est pas. (Cela ~str
et qui ~[T]
pouvaient être développés par magie, contrairement à tout autre ~
objet, était exactement pourquoi String
et a Vec<T>
été introduit, de sorte que les règles étaient toutes simples et cohérentes.)
Ils sont en fait complètement différents. Tout d'abord, a str
n'est rien d'autre qu'une chose de niveau 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 str
utilisée 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. A str
n'est conceptuellement qu'une ligne d' u8
octets avec la garantie qu'il forme un UTF-8 valide. 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>
fait exist lors de l' exécution. Il s'agit d'un soi-disant "gros pointeur"; c'est un pointeur avec des informations supplémentaires (dans ce cas, la taille de la chose qu'il pointe), il est donc deux fois plus grand. En fait, un &str
est assez proche d'un String
(mais pas d'un &String
). A &str
est deux mots; un pointeur vers le premier octet de a str
et un autre nombre qui décrit le nombre d'octets de la longueur str
.
Contrairement à ce qui est dit, a str
n'a pas besoin d'être immuable. Si vous pouvez obtenir un &mut str
en tant que pointeur exclusif vers le str
, vous pouvez le muter et toutes les fonctions sûres qui le mutent garantissent que la contrainte UTF-8 est respectée car si cela est violé, nous avons un comportement indéfini car la bibliothèque suppose que cette contrainte est vrai et ne le vérifie pas.
Alors qu'est-ce qu'un String
? Ça fait trois mots; deux sont les mêmes que pour &str
mais il ajoute un troisième mot qui est la capacité du str
tampon sur le tas, toujours sur le tas (a str
n'est pas nécessairement sur le tas) qu'il gère avant qu'il ne soit rempli et qu'il doive le réallouer. le possède enString
gros un comme on dit; il le contrôle et peut le redimensionner et le réallouer quand bon lui semble. Donc, comme dit plus près de a que de a .str
String
&str
str
Une autre chose est un Box<str>
; il 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 connaît pas sa capacité, donc en gros un Box<str>
peut être considéré comme une longueur fixe String
qui ne peut pas être redimensionnée (vous pouvez convertissez-le toujours en un 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.
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 facilement écrire des traits. En théorie str
, 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
est super utile pour pouvoir avoir plusieurs sous-chaînes différentes d'un String
sans avoir à copier; comme dit a String
possè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 devrait être copié 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 str
de la même chaîne. string
est celui qui possède le str
tampon complet réel sur le tas et les &str
sous-chaînes ne sont que de gros pointeurs vers ce tampon sur le tas.
std::String
est simplement un vecteur de u8
. Vous pouvez trouver sa définition dans le code source . Il est alloué en tas et évolutif.
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec<u8>,
}
str
est un type primitif, également appelé tranche de chaîne . Une tranche de chaîne a une taille fixe. Une chaîne littérale comme let test = "hello world"
has &'static str
type. test
est une référence à cette chaîne allouée statiquement.
&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 un petit changement en UTF-8 peut changer sa longueur d'octet, et une tranche ne peut pas réallouer son référent.
En termes simples, le String
type de données est-il stocké sur le tas (comme Vec
), et vous avez accès à cet emplacement.
&str
est un type de tranche. Cela signifie qu'il s'agit simplement d'une référence à un élément déjà présent String
quelque part dans le tas.
&str
ne fait aucune allocation lors de l'exécution. Donc, pour des raisons de mémoire, vous pouvez utiliser &str
over String
. Mais gardez à l'esprit que lors de l'utilisation, &str
vous devrez peut-être gérer des durées de vie explicites.
str
est view
déjà présent String
en tas.
Pour les personnes C # et Java:
String
===StringBuilder
&str
chaîne === (immuable) de RustJ'aime penser à un &str
comme une vue sur une chaîne, comme une chaîne internée en Java / C # où vous ne pouvez pas la changer, n'en créer qu'une nouvelle.
Voici une explication rapide et facile.
String
- Une structure de données évolutive et propriétaire pouvant être allouée en tas. Il peut être contraint à un &str
.
str
- est (maintenant, au fur et à mesure que Rust évolue) une chaîne mutable de longueur fixe qui vit sur le tas ou dans le binaire. Vous pouvez uniquement interagir avec str
un type emprunté via une vue de tranche de chaîne, telle que &str
.
Considérations d'utilisation:
Préférez String
si vous voulez posséder ou muter une chaîne - comme passer la chaîne à un autre thread, etc.
Préférez &str
si vous voulez avoir une vue en lecture seule d'une chaîne.
&str
est composé de deux composants: un pointeur sur certains octets et une longueur».