结构中的生命周期和类型说明符问题

Issue with lifetimes and type specifiers in struct

我正在努力解决一个有趣的问题,我发现它与结构定义中的生命周期和类型说明符有关。我想要做的是限制类型为 S 的所有字段 - 定义为 Into<Option<&str>>,这样我就可以将 Optionstr 作为值传递对于字段 - 生命周期 'a 定义为结构的生命周期。我正在使用 rustc 版本 1.58.1 以防有帮助。

这是我到目前为止的工作:

#[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:?}");
}

我想删除 'static 并将其限制为 'a。作为参考,我想创建类型为 S 的多个字段。我尝试了两种变体,但无法让它与任何一种一起使用。希望有人能够阐明我做错了什么。

变体 #1

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

错误:

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

变体 #2

按照建议尝试 where for.. here

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

错误:

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>`

一种方法是遵循编译器的建议并使用 PhantomData:

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

为了方便起见,你也可以有一个构造函数,这样你就不必重复PhantomData

另一种方法是解除对 impl 的限制,因为那里的规则不那么严格:

#[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 }
    }
}

我接受了 的建议,并决定将边界移动到传递结构的 impl(在本例中为函数)。所以这里的用例是该结构将用于构建具有可选字段的输入数据,然后将其传递给函数。

这解决了我的直接问题,但又是一个小问题,需要在我传入 input 的每个函数上重复边界。所以现在我直接在一个函数下添加所有逻辑,以避免在其他地方重复边界。

#[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);
}