Code learner
Code learner

Reputation: 304

Rust error : doesn't have a size known at compile-time

My understanding:

And just to differentiate datatypes returned from iter() and iter_mut(), we do &"Ferris" and &mut"Ferris"

Working code :

fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];
    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello!",
        }
    }
    println!("names: {:?}", names);
}

Why the below code is not working.

fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];
    for name in names.into_iter() {
        *name = match name {
            "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        };
        println!("names: {:?}", names);
    }
}
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
src/main.rs:6:25
  |
6 |             "Ferris" => "There is a rustacean among us!",
expected `str`, found `&str`

error[E0277]: the size for values of type `str` cannot be known at compilation time
src/main.rs:5:9
  |
5 |         *name = match name {
doesn't have a size known at compile-time
  |
help: the trait `Sized` is not implemented for `str`
note: the left-hand-side of an assignment must have a statically known size

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to 2 previous errors

I tried changing the &str to String using to_string() inside match statement but it's not clear why this code is not working.

Upvotes: 1

Views: 334

Answers (2)

cafce25
cafce25

Reputation: 27498

To understand the error you can look at the types, names is a Vec created from string literals, so it's type is Vec<&'static str>. Now the item of it's into_iter() iterator is &'static str, when you try do dereference that, i.e. write to *name like your code tries, it has to dereference &'static str, the result is str an unsized type! You could instead write to name but you'd have to make its binding mutable, of course into_iter moves it's receiver, the vector, so you cannot print it after that call so the result doesn't do all that much:

fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];
    for mut name in names.into_iter() {
        name = match name {
            "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        };
    }
}

into_iter called on collections, does not give you a "view" to some data, instead, it gives you the data directly, no pointer or other indirection, that's why dereferencing its items generally doesn't make sense (the big exception being when the collection contains pointers)


Here is an Overview of how the different *iter* methods work

Upvotes: 1

FreD
FreD

Reputation: 502

Types are not the same depending on your iteration. In particular name in names.into_iter() will not give you a mutable reference but the type inside collection names, that is &str.

Following code [playground]:

fn type_name<T>(_t: &T) -> &'static str {
    std::any::type_name::<T>()
}
 
fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];
    for name in names.iter_mut() {
        println!("names: {:?} -> {}", name, type_name(&name));
    }
    for name in names.iter() {
        println!("names: {:?} -> {}", name, type_name(&name));
    }
    for name in names.into_iter() {
        println!("names: {:?} -> {}", name, type_name(&name));
    }
}

will give you:

names: "Bob" -> &mut &str
names: "Frank" -> &mut &str
names: "Ferris" -> &mut &str
names: "Bob" -> &&str
names: "Frank" -> &&str
names: "Ferris" -> &&str
names: "Bob" -> &str
names: "Frank" -> &str
names: "Ferris" -> &str

Upvotes: 1

Related Questions