String
arrayIn Rust we often need to use fixed-sized collections of strings. We can use the str
primitive type, or the growable String
type.
To initialize the array, we can use an initializer expression. If we want growable strings, we may need the to_string
function.
To begin, we use the str
primitive type in this example. String
literals are of type str
—they cannot grow dynamically.
string
array of length 2. The elements "bird" and "frog" are added to the array at creation time.for
-loop over the animals array. We access each element and print it with println
.mut
keyword) and then print the elements again.fn main() { // Part 1: create string array. let mut animals: [&str; 2] = ["bird", "frog"]; // Part 2: loop over elements in string array. for animal in &animals { println!("For: {}", animal); } // Part 3: change element in string array. animals[0] = "cat"; println!("Element 0: {}", animals[0]); println!("Element 1: {}", animals[1]); }For: bird For: frog Element 0: cat Element 1: frog
String
arrayTo create an array of growable strings, we can use the String
type, and then call to_string()
on string
literals. These Strings can be appended to in-place.
String
array with an initializer expression. We call to_string()
on each element in the initialization.for
-loop over the colors array, and print each element with a println
function call.fn main() { // Part 1: create array of type String. // ... Use to_string for elements. let colors: [String; 2] = ["red".to_string(), "blue".to_string()]; // Part 2: print elements. for value in &colors { println!("For: {}", value); } }For: red For: blue
For string
arrays, we can use iter()
to loop over the array elements. Here we have an array of ID string
literals, and we use iter
to iterate them.
iter()
call as well as the type of the "ids" array—the program would still be the same.fn main() { let ids: [&str; 3] = ["x100", "y200", "z300"]; // Loop over the array by calling iter() which returns a slice. for value in ids.iter() { println!("ITER: {}", value); } }ITER: x100 ITER: y200 ITER: z300
String
Vec
Sometimes we want to create a collection of strings that we can add to—a string
Vec
is ideal here. We create the vector with vec
, and then can push items to the collection.
vec
!" macro to initialize the Vec
with 2 str
primitives. Then we call push()
to add 2 more strings.mut
keyword for a mutable vector: this allows us to change it by calling push()
.fn main() { let mut places = vec!["Paris", "New York"]; places.push("Madrid"); places.push("Toronto"); for place in &places { println!("PLACE: {}", place) } }PLACE: Paris PLACE: New York PLACE: Madrid PLACE: Toronto
We often need to pass arrays as arguments to functions. To receive an array argument, we specify its type in the formal parameter list.
test()
fn
receives a 2-element str
primitive array. It prints the length when in the function body.fn test(values: [&str; 2]) { // Receive string array as argument, print its length. println!("Length: {}", values.len()); } fn main() { let vegetables: [&str; 2] = ["carrot", "turnip"]; test(vegetables); }Length: 2
Vector
argumentIn the modify_vec
function, we pass a mutable reference to a string
vector. So we can modify the vector in the called function (and the program compiles).
Vec
as an argument, we can append many values to a vector (like bytes from a string
) in a function.fn modify_vec(values: &mut Vec<&str>) { // Modify the string vector argument. values.push("bird"); } fn main() { let mut animals = vec!["dog"]; // Pass a mut reference to allow vector to be modified in another function. modify_vec(&mut animals); println!("RESULT: {:#?}", animals); }RESULT: [ "dog", "bird", ]
How can we get an element from a String
vector? We can use the get()
function, which returns an Option
. If the index is in
-range, the element is returned.
if-let
syntax to get the inner value of the option as a local variable (here we call it "first").fn main() { // 1-element string vec. let mut values = vec![String::from("cat")]; // Use get on an element in the string vector. if let Some(first) = values.get(0) { println!("FIRST: {first}"); } // If the index is out of range, no panic occurs. if let Some(test) = values.get(400) { // Not reached. } }FIRST: cat
We can convert arrays and vectors to slices and use them in a unified way. Here the test()
function receives a string
slice.
string
vector, and then call as_slice()
to use the vector as a string
slice argument.fn test(values: &[&str]) { println!("SLICE LEN: {}", values.len()) } fn main() { // Part 1: use as_slice to convert Vector to slice. let shapes = vec!["square", "circle"]; test(shapes.as_slice()); // Part 2: use array as slice argument. let shapes2: [&str; 2] = ["square", "circle"]; test(&shapes2); // Part 3: get slice from array, and pass as argument. let shapes3: &[&str] = &["square", "circle"]; test(shapes3); }SLICE LEN: 2 SLICE LEN: 2 SLICE LEN: 2
Split
, Vec
We can place the results of the split()
function into a String
vector with collect()
. Here we get a String
vector from split and then pretty-print the results.
fn main() { let test = "abc def"; // Call split, and use collect() to get a string Vec. let values: Vec<&str> = test.split(' ').collect(); // Pretty-print the results. println!("VALUES: {:?}", values) }VALUES: ["abc", "def"]
Suppose we know how many Strings we want in a vector. We can provide that number to with_capacity
, and then push()
the Strings.
with_capacity
with an argument of 100.With_capacity
is a performance optimization as it reduces allocations and Vec
copies if the capacity was too low.Vec
of Strings, not str
references, so we call to_string
.String
instead of str
is easiest, and we can reduce copying with Arcs.fn main() { // Create a Vec of Strings, and use a capacity to avoid resizes. let mut birds = Vec::with_capacity(100); for _ in 0..100 { // We call to_string() to have String and not str references. birds.push("bird".to_string()); } println!("Bird count: {}", birds.len()); }Bird count: 100
Sometimes we want to use a for-in
loop over a String
Vec
or array in Rust. But we may also want the index for some reason.
enumerate()
to get index and element pairs from each position in an array.iter()
before calling enumerate()
.fn main() { let colors = vec!["blue", "green", "yellow"]; // Call enumerate to get index, value pairs in a for-loop. for (i, color) in colors.iter().enumerate() { println!("INDEX = {}, COLOR = {}", i, color); } }INDEX = 0, COLOR = blue INDEX = 1, COLOR = green INDEX = 2, COLOR = yellow
We can create a vector of a single string
repeated many times—think of this as a default value string
vector. The second "vec
" macro argument is the repeat count.
string
vector, which leaves its length as 0, so the is_empty()
function will return true.fn main() { // Create a string vector of 5 repeated strings. let mut repeated_values = vec!["A123".to_string(); 5]; println!("{:?}", repeated_values); // Clear the vector. repeated_values.clear(); println!("{:?}", repeated_values); // See if the vector is empty. if repeated_values.is_empty() { println!("IS EMPTY"); } }["A123", "A123", "A123", "A123", "A123"] [] IS EMPTY
To create string
arrays, we can use array syntax like any other array in Rust. But we must choose between string
primitives (str
) and Strings. Strings are more usable.