英文:
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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论