Reputation: 11612
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.
#[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
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>`
Upvotes: 2
Views: 84
Reputation: 11612
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);
}
Upvotes: 0
Reputation: 70970
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 }
}
}
Upvotes: 2