Tranches
Les tranches (slices) sont des références spéciales permettant d'emprunter une séquence d'éléments d'une collection plutôt que la collection complète. Une collection est une suite d'éléments: une chaîne de caractères, un vecteur, un tableau, etc.
Tranche de chaîne de caractère
Prenons l'exemple d'une fonction devant retourner le premier mot d'une chaîne de caractères constituée de mots séparés par des espaces:
fn first_word(s: &String) -> usize { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return i; } } s.len() } fn main() { let mut s = String::from("hello world"); let word = first_word(&s); // word will get the value 5 s.clear(); // this empties the String, making it equal to "" // word still has the value 5 here, but there's no more string that // we could meaningfully use the value 5 with. word is now totally invalid! println!("End index of first word: {}", word); }
Bien que correcte, cette implémentation n'est pas pertinente. En effet, même si
word
contient toujours 5 après l'appel à s.clear()
, cet index n'a plus de
sens.
Afin d'implémenter cette fonction de manière plus pertinente, il est plus judicieux d'utiliser un mécanisme de tranche:
fn main() { let s = String::from("hello world"); let hello = &s[0..5]; let world = &s[6..11]; println!("{}:{}", hello, world); }
La représentation en mémoire est la suivante:
Une tranche de chaîne est dénotée en Rust par &src
, ce qui nous permet de
réécrire la fonction précédente en:
fn first_word(s: &String) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..] } fn main() { let mut s = String::from("hello world"); let word = first_word(&s); // word will get the value 5 // s.clear(); // Incorrect ! println!("first word: {}", word); }
L'appel s.clear()
a été mis en commentaire car il est incorrect. En effet,
nous avons créé une référence immutable à s
lors de l'appel à first_word
,
transmise sous la forme d'une tranche à word
. Nous ne pouvons donc pas changer
s
par l'intermédiaire d'une référence mutable.
Les littéraux de type chaîne de caractère sont des tranches de type &str
:
fn first_word(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..] } fn main() { let hs = "hello world"; let my_string = String::from(hs); // `first_word` works on slices of `String`s, whether partial or whole let word = first_word(&my_string[0..6]); let word = first_word(&my_string[..]); // `first_word` also works on references to `String`s, which are equivalent // to whole slices of `String`s let word = first_word(&my_string); let my_string_literal = "hello world"; // `first_word` works on slices of string literals, whether partial or whole let word = first_word(&my_string_literal[0..6]); let word = first_word(&my_string_literal[..]); // Because string literals *are* string slices already, // this works too, without the slice syntax! let word = first_word(my_string_literal); }
Autres tranches
fn main() { let a = [1, 2, 3, 4, 5]; let slice = &a[1..3]; assert_eq!(slice, &[2, 3]); }