这是"dropped here while still borrowed" when making lifetime explicits的后续,但可以独立查看。
既然@jthulhu让我发现了修复我之前错误的高阶特质界限,我想更进一步推广它。让我们创建一个trait:
trait ToImplement {
type Arg<'arg>;
fn do_something<F>(&self, f: F)
where
F: for<'b> FnOnce(Self::Arg<'b>) -> ();
}
字符串
这个trait可以毫无问题地实现,并且当直接使用时,可以按照预期工作,如这个测试所示:
impl ToImplement for String {
type Arg<'arg> = &'arg str;
fn do_something<F, T>(&self, f: F) -> T
where
F: for<'b> FnOnce(Self::Arg<'b>) -> T,
{
f(&self)
}
}
#[test]
fn works() {
let hello = String::from("Hello");
let r = hello.do_something(|s| format!("{s} world!"));
assert_eq!(r,"Hello world!")
}
型
现在,让我们尝试编写一个使用方法do_something
的函数,不知道实现者,但知道Arg
类型的一些约束,以便我们可以使用它。
10 │ fn go<D, I>(implementor: I)
11 │ where
12 │ D: Display,
13 │ for<'a> I: ToImplement<Arg<'a> = D>,
14 │ {
15 │ implementor.do_something(|a| println!("{a}"));
16 │ }
型
这确实可以正确编译,但如果我们尝试像这样使用它:
33 │ #[test]
34 │ fn works() {
35 │ let hello = String::from("Hello");
36 │ go(hello);
37 │ }
型
然后我们得到这个错误:
error[E0308]: mismatched types
|
36 | go(hello);
| ^^^^^^^^^ one type is more general than the other
|
= note: expected reference `&'a str`
found reference `&str`
note: the lifetime requirement is introduced here
|
13 | for<'a> I: ToImplement<Arg<'a> = D>,
|
型
我认为我在第13行声明生命周期'a
的方式是错误的。但我不知道我还能怎么做。
我读了@jthulhu指出的关于所有权的rustonomicon章节,并认为Borrow splitting会给我一些答案,并查看了Take的实现,但没有更高级别的特质界限。
1条答案
按热度按时间ej83mcc01#
经过大量的挖掘,我最终在RFC中找到了一个解决方案:https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md#using-associated-type-constructors-in-bounds
诀窍是将
Arg
的生存期与ToImplement
分开声明,如下所示:字符串
现在,它看起来像是编译器推断类型的问题,所以我们必须在调用函数时定义它:
型