英文:
Lifetime deduction and borrowing in generic functions (required for generic-tests)
问题
以下是翻译好的代码部分:
假设有一个简单的特性,允许附加对象引用。这里我使用`String`来使代码稍微简化。
trait Attachable<'a> {
fn new() -> Self;
fn attach(&mut self, value: &'a String);
}
一个简单的实现可能如下所示:
```rust
struct SomeAttachable<'a> {
    id: Option<&'a String>,
}
impl<'a> Attachable<'a> for SomeAttachable<'a> {
    fn new() -> Self {
        Self { id: None }
    }
    fn attach(&mut self, value: &'a String) {
        self.id = Some(value);
    }
}
并且可以直接使用它。
let mut object = SomeAttachable::new();
let value = "hello".to_string();
object.attach(&value);
但是当将它放入一个泛型函数中,仅提供可附加类型时,会出现问题。
fn do_stuff<'a, T: Attachable<'a>>() {
    let mut object = T::new();
    let value: String = "hello".to_string();
    object.attach(&value); // 这里会出现问题,因为value的生命周期不够长
}
我认为生命周期在调用函数do_stuff时被检测,然后value有一个“错误”的生命周期要求。如何在do_stuff的实现中纠正生命周期问题。
将函数签名调整为:
fn do_stuff_with_argument<'a, T: Attachable<'a>>(value: &'a String) {
    let mut bla = T::new();
    bla.attach(value);
}
将解决该问题,因为现在生命周期再次被正确检测,因为它是输入引用参数的一部分。但这对我来说不是一个合适的解决方案。do_stuff函数应在函数内部处理所有逻辑,而不需要任何函数参数。只允许泛型参数,如生命周期和类型。
我认为我可能需要使用Higher-Rank Trait Bounds并像这样实现do_stuff:
fn do_stuff<T>()
where
    T: for<'a> Attachable<'a>,
{
    let mut object = T::new();
    let value: String = "hello".to_string();
    object.attach(&value);
}
但这会导致Rust抱怨Attachable的实现对于SomeAttachable来说不够泛型。
为了提供一些上下文:我需要这个用于基于generic-test crate的单元测试,其中验证了特性的行为。
英文:
Assume a simple trait which allows an object reference to be attached. I used String here to make the code a little bit simpler.
trait Attachable<'a> {
    fn new() -> Self;
    fn attach(&mut self, value: &'a String);
}
A simplistic implementation could look like:
struct SomeAttachable<'a> {
    id: Option<&'a String>,
}
impl<'a> Attachable<'a> for SomeAttachable<'a> {
    fn new() -> Self {
        Self { id: None }
    }
    fn attach(&mut self, value: &'a String) {
        self.id = Some(value)
    }
}
and using it works out of the box.
let mut object = SomeAttachable::new();
let value = "hello".to_string();
object.attach(&value);
But when this is put into a generic function where only the attachable type is provided it breaks.
fn do_stuff<'a, T: Attachable<'a>>() {
    let mut object = T::new();
    let value: String = "hello".to_string();
    object.attach(&value); // breaks here since value does not live long enough
}
I assume that the lifetime is detected when the function do_stuff is called and then value has a "wrong" lifetime requirement. How can I correct the lifetime issue in the implementation do_stuff.
Adjusting the function signature to:
fn do_stuff_with_argument<'a, T: Attachable<'a>>(value: &'a String) {
    let mut bla = T::new();
    bla.attach(&value);
}
would solve the problem since now the lifetime is detected correctly again since it is part of the input reference argument. But this wouldn't be a suitable solution for me. The do_stuff function shall handle all the logic inside the function without requiring any function arguments. Only generic arguments are allowed like lifetimes and types.
I assume that I may have to use Higher-Rank Trait Bounds and implement do_stuff like:
fn do_stuff<T>()
where
    T: for<'a> Attachable<'a>,
{
    let mut object = T::new();
    let value: String = "hello".to_string();
    object.attach(&value);
}
but this causes rust to complain that the implementation of Attachable is not generic enough for SomeAttachable.
To provide some context: I require this for unit tests based on the generic-test crate where the behavior of a trait is verified.
答案1
得分: 2
你可以使用一个AttachableTag特质,它具有一个通用的关联类型,为任何'a提供了一个Attachable<'a>(你也可以使用相同的特质和类型,但我更喜欢这种方式):
trait AttachableTag {
    type Attachable<'b>: Attachable<'b>;
}
trait Attachable<'a> {
    fn new() -> Self;
    fn attach(&mut self, value: &'a String);
}
struct SomeAttachableTag;
impl AttachableTag for SomeAttachableTag {
    type Attachable<'b> = SomeAttachable<'b>;
}
struct SomeAttachable<'a> {
    id: Option<&'a String>,
}
impl<'a> Attachable<'a> for SomeAttachable<'a> {
    fn new() -> Self {
        Self { id: None }
    }
    fn attach(&mut self, value: &'a String) {
        self.id = Some(value)
    }
}
fn do_stuff<T: AttachableTag>() {
    let mut object = T::Attachable::new();
    let value: String = "hello".to_string();
    object.attach(&value);
}
do_stuff::<SomeAttachableTag>();
然而,这会导致错误:
error[E0597]: `value` does not live long enough
  --> src/main.rs:32:19
   |
32 |     object.attach(&value);
   |                   ^^^^^^ borrowed value does not live long enough
33 | }
   | -
   | |
   | `value` dropped here while still borrowed
   | borrow might be used here, when `object` is dropped and runs the destructor for type `<T as AttachableTag>::Attachable<'_>`
   |
   = note: values in a scope are dropped in the opposite order they are defined
要解决这个问题,将value的声明移动到object之前:
fn do_stuff<T: AttachableTag>() {
    let value: String = "hello".to_string();
    let mut object = T::Attachable::new();
    object.attach(&value);
}
英文:
You can use a AttachableTag trait, with a generic associated type that gives you an Attachable<'a> for any 'a (you can also use the same trait and type, but I prefer it this way):
trait AttachableTag {
    type Attachable<'b>: Attachable<'b>;
}
trait Attachable<'a> {
    fn new() -> Self;
    fn attach(&mut self, value: &'a String);
}
struct SomeAttachableTag;
impl AttachableTag for SomeAttachableTag {
    type Attachable<'b> = SomeAttachable<'b>;
}
struct SomeAttachable<'a> {
    id: Option<&'a String>,
}
impl<'a> Attachable<'a> for SomeAttachable<'a> {
    fn new() -> Self {
        Self { id: None }
    }
    fn attach(&mut self, value: &'a String) {
        self.id = Some(value)
    }
}
fn do_stuff<T: AttachableTag>() {
    let mut object = T::Attachable::new();
    let value: String = "hello".to_string();
    object.attach(&value);
}
do_stuff::<SomeAttachableTag>();
However, this gives an error:
error[E0597]: `value` does not live long enough
  --> src/main.rs:32:19
   |
32 |     object.attach(&value);
   |                   ^^^^^^ borrowed value does not live long enough
33 | }
   | -
   | |
   | `value` dropped here while still borrowed
   | borrow might be used here, when `object` is dropped and runs the destructor for type `<T as AttachableTag>::Attachable<'_>`
   |
   = note: values in a scope are dropped in the opposite order they are defined
To fix that, move value's declaration before object:
fn do_stuff<T: AttachableTag>() {
    let value: String = "hello".to_string();
    let mut object = T::Attachable::new();
    object.attach(&value);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论