2

Im wrestling with an interesting problem I found with lifetimes and type specifiers in a struct definition. What I'm trying to do is restrict all fields with type S - which is defined as Into<Option<&str>>, so that I can pass either an Option or str as a value for the field - to a lifetime 'a which is defined as the struct's lifetime. I'm using rustc version 1.58.1 in case it helps.

Here's what I got working so far:

#[derive(Debug)]
struct A<S>
where
    S: Into<Option<&'static str>>,
{
    my_field: S,
}

fn main() {
    let obj = A {
        my_field: "hello world",
    };
    println!("{obj:?}");
}

I want to remove the 'static and restrict it to 'a. For reference I want to create multiple fields with a type S. I tried two variants but unable to get it working with either. Hopefully someone is able to shed light on what I'm doing wrong.

Variant #1

#[derive(Debug)]
struct A<'a, S>
where
    S: Into<Option<&'a str>>,
{
    my_field: S,
}

error:

error[E0392]: parameter `'a` is never used
  |
2 | struct A<'a, S>
  |          ^^ unused parameter
  |
  = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData

Variant #2

Trying where for.. as suggested here

#[derive(Debug)]
struct A<S>
where
    for<'a> S: Into<Option<&'a str>>,
{
    my_field: S,
}

error:

error: implementation of `From` is not general enough
   |
10 |     let obj = A {
   |               ^ implementation of `From` is not general enough
   |
   = note: `Option<&'0 str>` must implement `From<&str>`, for any lifetime `'0`...
   = note: ...but it actually implements `From<&'a str>`
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
  • It's probably not what you want to put the bounds on the struct. See [Should trait bounds be duplicated in struct and impl?](https://stackoverflow.com/questions/49229332/should-trait-bounds-be-duplicated-in-struct-and-impl) – Chayim Friedman Feb 15 '22 at 00:14
  • @ChayimFriedman yep, this is a good point. I can remove the bounds on the struct as I plan to use the struct as input to functions. But then I realized the problem there is I need to add generic type like `S1: Into – rv.kvetch Feb 15 '22 at 00:26

2 Answers2

2

One way is to follow the compiler's suggestion and use PhantomData:

#[derive(Debug)]
struct A<'a, S>
where
    S: Into<Option<&'a str>>,
{
    my_field: S,
    _marker: PhantomData<&'a ()>,
}

You can also have a constructor, for convience, so you don't have to repeat the PhantomData.

Another way is to lift the bound to the impl, because the rules are less strict there:

#[derive(Debug)]
struct A<S> {
    my_field: S,
}

impl<'a, S> A<S>
where
    S: Into<Option<&'a str>>
{
    fn new(s: S) -> Self {
        Self { my_field: s }
    }
}
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Unfortunately I can't use the `PhantomData` suggestion because the plan is to expose the struct as `pub` to the client. The use case is to use the struct to build input data, and then pass into a function. But I like your other idea of restricting further down, like say an `impl`. – rv.kvetch Feb 15 '22 at 00:39
0

I went with suggestion by @Chayim Friedman and decided to move the bounds to the impl (in this case a function) where the struct is passed in to. So the use case here is that the struct would be used to build input data with optional fields, then pass it into a function.

This solves my immediate issue, but again is slight issue with the need to repeat the bounds on each function where I pass in input to. So for now I am adding all the logic under the one function directly, to avoid duplicating the bounds in other places.

#[derive(Debug)]
struct InputStruct<S1, S2> {
    my_field: S1,
    another_field: S2,
}

fn do_something_with_struct<'a, S1: Into<Option<&'a str>>, S2: Into<Option<&'a str>>>(
    input: InputStruct<S1, S2>,
) {
    // do something with input data
    let opt1 = input.my_field.into();
    let opt2 = input.another_field.into();
}

fn main() {
    let obj = InputStruct {
        my_field: "hello world",
        another_field: None,
    };
    println!("{obj:?}");

    do_something_with_struct(obj);
}
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53