Most clean: Using to_string() or lifetimes?

What is the most clean?

        // Create data object we will return.
        #[derive(Serialize)]
        struct Data<'a> {
            id: &'a String,
            description: &'a String,
            start_funds: f64,
            current_funds: f64,
        }

        let data = Data {
            id: &algorithm.id,
            description: &algorithm.description,
            start_funds: algorithm.start_funds,
            current_funds: algorithm.get_current_funds(psql).await.unwrap(),
        };

Or

        // Create data object we will return.
        #[derive(Serialize)]
        struct Data {
            id: String,
            description: String,
            start_funds: f64,
            current_funds: f64,
        }

        let data = Data {
            id: algorithm.id.to_string(),
            description: algorithm.description.to_string(),
            start_funds: algorithm.start_funds,
            current_funds: algorithm.get_current_funds(psql).await.unwrap(),
        };

Don't use &'a String, it adds an unnecessary level of indirection. Use &'a str instead.

As for your question, use the former if you want to save a few bytes of memory, but in general I don't like to have structs with lifetimes due to the additional complexity that comes when handling them.

1 Like

Using a String reference in a struct (which should be 'a &str as @moy2010 said) only makes sense when the struct is not the logical owner of the Strings, and it is important to avoid the cost of cloning the Strings (which involves allocating and copying), and it is possible to do this without violating Rust's ownership rules.

This example is probably contrived, but think about a Vec of 10,000 large Strings, Vec<String>. This Vec is the owner. Now imagine that you want to temporarily sort the Strings in a different order for some reason, and while you're doing this you don't need to modify the Strings.

To do this it makes sense to create a temporary new Vec containing references to the Strings (Vec<'a &str>) and sort the temporary Vec. The temporary Vec will have to be allocated, but the Strings will not have to be cloned.

The Strings cannot be changed until the temporary Vec is dropped (no longer used) because you can't mutate something while a reference to it is still being used.

Note that I didn't come up with a great example and that is probably because this is not very commonly needed. If in doubt, use owned objects instead of references inside data structures.

Perhaps a more interesting example is storing references in data structures when using an arena allocator, but that's a more advanced use case.