tldr;> 给定一个接受通用回调参数并返回关联类型的特征函数,编译器会抱怨关联类型可能从回调函数借用参数。有没有办法告诉编译器事实并非如此?
详情: 我计划实现一个接受回调参数的特征函数,并希望强制该特征函数的实现实际调用该回调。我通过让回调返回一个只能通过调用此回调来构造的类型并强制特征函数返回该类型来实现此目的。
/// If this type is used as a generic argument in a trait function and that trait function
/// returns `CallbackResult`, then that enforces that any implementation of that
/// trait function must call the callback.
pub trait Callback<T> {
type CallbackResult;
fn call(self, v: T) -> Self::CallbackResult;
}
如何使用它的示例:
trait MyTrait {
fn func<C>(&self, callback: C) -> C::CallbackResult
where
C: Callback<i32>;
}
struct MyStruct {}
impl MyTrait for MyStruct {
fn func<C>(&self, callback: C) -> C::CallbackResult
where
C: Callback<i32>,
{
// The compiler forces us to call callback because that's the only way
// for us to create a value of type C::CallbackResult.
let v = 42;
callback.call(v)
}
}
这有效:游乐场
当回调需要引用时,问题就出现了:
trait MyTrait {
fn func<C>(&self, callback: C) -> <C as Callback<&i32>>::CallbackResult
where
C: for <'a> Callback<&'a i32>;
}
struct MyStruct {}
impl MyTrait for MyStruct {
fn func<C>(&self, callback: C) -> <C as Callback<&i32>>::CallbackResult
where
C: for<'a> Callback<&'a i32>,
{
// The compiler forces us to call callback because that's the only way
// for us to create a value of type C::CallbackResult.
let v = 42;
callback.call(&v)
}
}
现在失败了:
error[E0515]: cannot return value referencing local variable `v`
--> src/lib.rs:22:5
|
22 | callback.call(&v)
| ^^^^^^^^^^^^^^--^
| | |
| | `v` is borrowed here
| returns a value referencing data owned by the current function
For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground` (lib) due to previous error
所以看来 Rust 假设
CallbackResult
可能保留对 v
的引用。有没有办法告诉编译器它没有?我尝试向关联类型添加绑定 CallbackResult: 'static
,但这没有什么区别。
更高种类的类型目前受到 rust 借用检查器的限制,从生成的错误可以看出here。
您可以通过直接使用函数特征来实现您想要的悬停:
trait MyTrait {
fn func<R>(&self, callback: impl FnOnce(&i32) -> R) -> R;
}
struct MyStruct {}
impl MyTrait for MyStruct {
fn func<R>(&self, callback: impl FnOnce(&i32) -> R) -> R {
let v = 42;
callback(&v)
}
}
fn main() {
let s = MyStruct{};
s.func(|v| println!("{v}"));
}