fn main() {
let mut colors = vec!["red".to_owned(), "orange".to_owned(), "yellow".to_owned()];
for color in &mut colors {
color.make_ascii_uppercase();
}
println!("{:?}", colors);
}
The key here is that &mut [T] implements IntoIterator<Item = &mut T>, so you can use it in a for loop. The loop variable color has type &mut String.
The problems with your original code are
for color in &colors causes the loop variable to have type &&str (immutable reference to &str) -- because &[T]: IntoIterator<Item = &T> -- which does not allow modifying the Vec entries
you can't put a reference to upper into *color (even if you had a mutable reference instead) because upper is dropped at the end of each loop iteration
Another way to do it is to replace for color in &mut colors with for color in colors.iter_mut(), where iter_mut is an inherent method of [T] that returns an iterator over &mut T. iter_mut (and the immutable version iter) are useful in method chains.
To add a little detail to this portion -- this is because your original Vec was a Vec<&str>. The original contents were all &'static str, with the data baked into the binary. The updated contents would have to be stored somewhere (as Strings) at run time for you to put references to them (&'a str) into your Vec. (Or in some static area that you unsafely mutate, but that's not great so let's ignore it here.)
Thus @cole-miller's change to store Vec<String> instead, via .to_owned().
It seems every time I start thinking I'm getting the hang of Rust I get tripped up on another seemingly simple thing. I see your solution works, but I wanted to see if I could avoid typing out .to_owned or .to_string on each color. So I tried this:
fn main() {
let original = vec!["red", "orange", "yellow", "green", "blue", "purple"];
let colors = original.iter().map(|c| c.to_string()).collect::<[String]>();
// Without the turbofish on the previous line I get an error on the next line that says
// "the element type for this iterator is not specified".
// I don't understand why the compiler can't infer that it is an Iterator over String values.
// What else could it be?
// With the turbofish I get
// "a value of type `[String]` cannot be built from an iterator over elements of type `String`".
// It seems like that is exactly what the collect method should do.
for color in colors {
color.make_ascii_uppercase();
}
println!("{:?}", colors);
}
[String] represents a variably-sized contiguous "array" of memory, which in Rust is called a slice. That's it: there is no indirection unless it is explicitly written. For technical reasons, such things / entities cannot be moved around, stored, etc., without indirection. So what you will often see is:
a (potentially) shared borrow (reference) to a slice: &'_ [String];
an exclusive (thus allowing mutation) borrow (reference) to a slice: &'_ mut [String];
an exclusive and owned heap-allocated pointer to a slice: Box<[String]>
And more exotic stuff, such as a (potentially) shared and yet owned heap-allocated pointer to a slice (thanks to reference-counting): Arc<[String]> (or Rc as a single-threaded micro-optimization).
In your case, you are collecting / creating something, so you'll be owning it exclusively, thus Box<[String]> is the more apt candidate.
That being said, Box pointers are always fully initialized, which makes it (again, for the same variably-sized reasons (!Sized / not-Sized in Rust parlance)), hard and next to impossible to grow these "arrays" / buffers. For that, there is a more flexible equivalent of Box<[String]>:, which is our good old Vec<String>. Since .collect()ing is an operation that will keep accumulating / aggregating stuff into a buffer, Vec is a more idiomatic type to be using.
Thus, a solution is to do the following:
fn main() {
let original = vec!["red", "orange", "yellow", "green", "blue", "purple"];
- let colors = original.iter().map(|c| c.to_string()).collect::< [String] >();
+ let colors = original.iter().map(|c| c.to_string()).collect::< Vec<String> >();
Thanks for explaining that! That helps, but I what I really want to do is uppercase a subset of the colors. Here is what I have now:
fn main() {
let original = vec!["red", "orange", "yellow", "green", "blue", "purple"];
let colors = original.iter().map(|c| c.to_string()).collect::<Vec<String>>();
let subset = colors[1..=3]; // error: the size ... cannot be known at compilation time
for color in subset {
color.make_ascii_uppercase();
}
println!("{:?}", colors);
}
fn main() {
let original = vec!["red", "orange", "yellow", "green", "blue", "purple"];
let mut colors = original.iter().map(|c| c.to_string()).collect::<Vec<String>>();
let subset = &mut colors[1..=3];
for color in subset {
(*color).make_ascii_uppercase();
}
println!("{:?}", colors);
}
You can remove the explicit dereference since the auto-deref will take care of that. You never need to derefence in order to call a method unless there's some ambiguity.
Just, to reconnect to my previous post: you wanted to iterate and mutate a subset of the slice. To iterate, you did correctly use the for elem in … syntax, but forgot to use indirection, as I just mentioned.
Now, regarding which kind of indirection to use,
here you will be borrowing the slice (since you want the Vec to keep existing after the for loop / you don't want the for loop to take ownership of the Vec), so & or &mut it is;
and given that you own and thus have exclusive access over the Vector, you can use an exclusive borrow (&mut) to get mutation capabilities, hence the &mut colors[<range>] idiom.