Generics, lifetimes and a slice

How to use a struct with generic lifetimes and a slice?
In this case first and second would be &&str instead of &str.
Is there any ways of making MyStruct instance to have &'a str and &'b str?

struct MyStruct<'a, 'b, T> {
    first: &'a T,
    second: &'b T
}

struct Wrapper<'a, 'b> {
    slice: MyStruct<'a, 'b, &'a str>
}

As followed by the advice from the compiler:

error[E0277]: the size for values of type `str` cannot be known at compilation time
 --> src/main.rs:7:12
  |
7 |     slice: MyStruct<'a, 'b, str>
  |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `Sized` is not implemented for `str`
note: required by a bound in `MyStruct`
 --> src/main.rs:1:25
  |
1 | struct MyStruct<'a, 'b, T> {
  |                         ^ required by this bound in `MyStruct`
help: consider relaxing the implicit `Sized` restriction
  |
1 | struct MyStruct<'a, 'b, T: ?Sized> {
  |                          ++++++++

you can do this:

struct MyStruct<'a, 'b, T: ?Sized> {
    first: &'a T,
    second: &'b T
}

struct Wrapper<'a, 'b> {
    slice: MyStruct<'a, 'b, str>
}

playground

3 Likes

General warning, because it cannot be said often enough:

Structs with too many lifetime arguments (or any lifetime arguments, really) can be hard to use. At a minimum, make sure to double-check that you are aware of what “ownership” conceptually is, and that you really want to store – usually shorter-lived – borrows in your struct fields. I.e.: every user of your structs would handle ownership of those values themself, storing the necessary values (e.g. the strings in this use-case) in their own local variables (or in other struct(s) with owning semantics, or another place higher-up on the call-stack), and always drops your MyStruct or Wrapper values before those owners go out of scope. If use-cases don’t look like that, fields that own the values, or shared-ownership with Rc or Arc might be more appropriate.

Also make sure to be even more wary (and double-check again that you got your ownership story clear…[1]) once any &'a MyStruct<'a, …> style values appear in your code, the same lifetime appears both in a type parameter and as the lifetime of a reference borrowing that type, and make sure to (essentially[2]) never let &mut 'a MyStruct<'a, …>, i.e. the same thing with a mutable reference, appear.


  1. e.g. you aren’t trying to write a recursive type? (Those would be quite inconvenient to use without owning fields, typically only read-only and involving lots of manually managed owners, e.g. lots of local variables for every recursive step.) ↩︎

  2. if you do need such a mutable reference, you most likely know who you are and what you’re doing, so why are you reading this? :slight_smile: ↩︎

2 Likes

Thank you!

Yes, I understand that. My thinking is the following: the struct will be read-only, so I can save some memory using &str instead of String.

Read-only is one part of the equation; even so, use-cases will need to involve the caller of your struct’s APIs to save and keep the String around somewhere. This is most commonly not a problem when your struct only exists for a short time, e.g. as some intermediate state/value in a sequence of function calls.

Of course, your use-case might be fitting. I’m also adding this information for others that might come across this thread, as we’re in a public forum :slight_smile:


Another tip I forgot: Writing test cases early, perhaps even while some method implementations are still todo!()-implemented, can help ensure that API one defines that involves a bunch of lifetimes, is actually usable as intended. There is always two parts to function signatures with lifetimes: The implementation needs to support the lifetimes, and the intended use cases need to do so. If the use-cases turn out to be easier to write than the implementation, doing them first is a way to weed out earlier one of the possibilities for error.

1 Like

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.