Why is 'static used?

Hello

Why does this example for traits, use 'static for the name field in the structs e.g Sheep?

Why isn't &str sufficient?

For simplicity.

Because “simple” tracing-GC based references with megabytes of runtime and insane complexity “under the hood” don't exist in Rust. And if you would use &'a str with a lifetime 'a then Rust would immediately ask bazillion questions about who creates that reference, who owns them, how long they do exist and so on.

And 'static is simple answer to all that: that is reference to immutable object that lives forever. Bam. Done. We can talk other aspects and return back to 'a later.

1 Like

To avoid a lifetime parameter probably (i.e. for simplicity). They could have used String. Or Cow<'static, str>.

Note that the type of the field isn't germane to the example; the author probably didn't give it much thought at all.

If you mean, why isn't 'static the default when elided in struct definitions, the reason is that it's not clear 'static is a reasonable default for struct definitions (in contrast with static values say, which must be 'static for soundness).

If you mean, why doesn't this

struct Sheep { naked: bool, name: &str }

mean this

struct Sheep<'a> { naked: bool, name: &'a str }

the reason is that having a parameter is a big deal and not something to make less obvious or to be able to introduce accidentally; moreover, you'd have to decide on the order of multiple inferred parameters, which would probably make it a breaking change to reorder the struct definition code, etc.

3 Likes

Is it wrong to just use String all the time?

For fields? It's generally perfectly fine and usually what you want.

3 Likes

&str isn't sufficient, because it's not just a string. It's a special case of a view into data of another string allocated elsewhere. It's super important to track where that data is, because &str itself cannot exist without it, and must not be allowed to exist after data it references has been freed by something else in the program.

'static is a special case that means the data will never be freed in this program (it's leaked memory, or data hardcoded into the executable). Only in this one case there's no worrying about the danger of the referenced data being freed too soon.

&'static str is used here to make the example code a bit shorter, because it's the exact type of string literals. Real code should generally use String for struct fields. String isn't borrowing data from anywhere else, and it can live for as long as it wants.

5 Likes

Okay thanks for the answers everyone. So in this example 'static is just used to simplify the example?

And for e.g this code:

fn function(map: &mut std::collections::HashMap<&str, &str>) {
    let c_str = "c".to_string();
    
    map.insert("key3", &*c_str);

    return;
}

fn main() {
    let mut map = std::collections::HashMap::from([
        ("key1", "a"),
        ("key2", "b"),
    ]);
    
    function(&mut map);
}

It yields error[E0597]:c_str does not live long enough (what I understand).

But also here it would just be advised to make the type of the hashmap HashMap<String, String>?

I find it a bit ugly to do:

  let mut map = std::collections::HashMap::from([
        ("key1".to_string(), "a".to_string()),
        ("key2".to_string(), "b".to_string()),
    ]);

I would not say all the time. But if you are storing the data in the structs I would think it is preferable for the struct to own it, i.e. use String.

If that example got the names of sheep from some user input or wherever then those names as static str would not exist and Strings would have to be used.

Personally I think the example would be better written using Strings for clarity. That only means adding a few .to_string()' and a clone()`. Which is appropriate given the example uses "Dolly" the sheep :slight_smile:

1 Like

In Rust conversions that have a cost are always explicit. In this case the cost is the allocation of the string on the heap. A String has data that lives on the heap, while a &'static str normally refers to a constant.

It may be slightly cleaner to use into rather than to_string, which is possible if you specify the HashMap generic types:

use std::collections::HashMap;
let mut map: HashMap<String, String> = HashMap::from([
    ("key1".into(), "a".into()),
    ("key2".into(), "b".into()),
]);

More importantly, if only constant strings are stored in the Map then by all means use &'static str as the key and value types as in the original version. But then function will have to store "c" instead of "c".to_string(). All the values in the map have to be one type or the other. Here is the example changed to always use string constants:

    use std::collections::HashMap;
    fn main() {
        let mut map = HashMap::from([("key1", "a"), ("key2", "b")]);
        function(&mut map);
    }
    fn function(map: &mut HashMap<&'static str, &'static str>) {
        let c_str = "c";
        map.insert("key3", c_str);
        return;
    }

EDIT: Also note that you can create a map with &'static str keys and String values.

1 Like