In a struct why must I use &str and a lifetime

Hello,

Im doing the rustlings and I m not at a point I solved the challenge but not really understand why

I have this code :

struct ColorClassicStruct<'a> {

   name: &'a str ,

   hex: &'a str

}

struct ColorTupleStruct<'a> (& 'a str,& 'a str);

Can someone explain why it must be &str and not String and why it needs a lifetime

Could you share the challenge description? Usually it's not necessary to use &str over String, unless you do some optimisation. Also, please pay attention to the pinned thread.

3 Likes

You can put a String inside a struct (and any user-defined type, in fact), it doesn't have to be a &str just because it's in a struct. In fact it's often more advisable to prefer owning types over borrowed types while you are learning the language.

The &str needs a lifetime because it's a borrowed (reference) type, and when it's part of another type, the compiler needs some annotation as to how long you want the referred-to string slice to live at least. That is, a struct may be constructed, used, and destroyed in any number of ways, and the compiler can't infer the underlying lifetime based on the type definition alone.

In contrast when you actually use a &str in executable code (ie. a function), its lifetime will be inferred more often than not based on the way you use it.

of course I can , This is all we had :

// structs1.rs

// Address all the TODOs to make the tests pass!

// I AM NOT DONE

struct ColorClassicStruct<'a> {

   name: &'a str ,

   hex: &'a str

}

struct ColorTupleStruct<'a> (& 'a str,& 'a str);

#[derive(Debug)]

struct UnitStruct;

#[cfg(test)]

mod tests {

    use super::*;

    #[test]

    fn classic_c_structs() {

        // TODO: Instantiate a classic c struct!

        let green = ColorClassicStruct{name: "green", hex:"#00FF00"};

        assert_eq!(green.name, "green");

        assert_eq!(green.hex, "#00FF00");

    }

    #[test]

    fn tuple_structs() {

        // TODO: Instantiate a tuple struct!

        let green = ColorTupleStruct("green", "#00FF00");

        assert_eq!(green.0, "green");

        assert_eq!(green.1, "#00FF00");

    }

    #[test]

    fn unit_structs() {

        // TODO: Instantiate a unit struct!

        let unit_struct = UnitStruct; 

        let message = format!("{:?}s are fun!", unit_struct);

        assert_eq!(message, "UnitStructs are fun!");

    }

}

In this case you need &str b/c that's what tests are calling for. And b/c you have to use &str you have to supply lifetime annotation. If you want to use String in the structs, you need to append .to_string() to all the strs in the tests.

String literals, for example "green", have the type &'static str in Rust.

'static means that the reference is valid for the rest of the program. This is the case for a string literal because it is a reference to data within the binary that will never change or move.

You should not use temporary borrows in structs, because that makes the whole struct temporary and bound to a scope of its content. It will cause you a lot of pain with the borrow checker.

In structs use owned values, such as String.

3 Likes

This appears to be the template you started with: rustlings/structs1.rs at 55a9284665dc6ef5d9db6f73e76630f22d3ea43d · rust-lang/rustlings · GitHub

Given this code, I would have absolutely ended up with the following:

struct ColorClassicStruct {
    name: String,
    hex: String,
}

impl ColorClassicStruct {
    fn new(name: &str, hex: &str) -> Self {
        Self {
            name: name.into(),
            hex: hex.into(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn classic_c_structs() {
        let green = ColorClassicStruct::new("green", "#00FF00");

        assert_eq!(green.name, "green");
        assert_eq!(green.hex, "#00FF00");
    }
}

In conclusion, what you wrote works to complete this particular challenge, but is not idiomatic and will not work in a myriad of other situations.

1 Like

Thanks,

I did not learn this but I think I understand it

impl ColorClassicStruct {
    fn new(name: &str, hex: &str) -> Self {
        Self {
            name: name.into(),
            hex: hex.into(),
        }
    }
}

but what does the impl means ?

impl Struct { ...} means "implement the following methods for the Struct type."
See the book here:
https://doc.rust-lang.org/book/ch05-03-method-syntax.html

2 Likes

FYI, if you do not intend to edit the string in the struct, Box<str> conveys this intention better than String. String is a string buffer, i.e. it has a length, as well as a capacity.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.