我想制作一个类似于以下结构的特征(我的特殊用例更为复杂,但这捕获了我遇到的问题和错误(。我遇到的问题是最后一生中的一生。我认为我需要将它们挤入特征定义中,但我不确定如何。我该如何整理一生,所以这会编译?
Rust Playground链接到代码
trait MyTrait<TIn> {
fn f<TOut, F>(f: F, x: Self) -> TOut
where
F: Fn(TIn) -> TOut;
}
impl<T> MyTrait<T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: Fn(T) -> TOut,
{
f(x)
}
}
impl<T> MyTrait<T> for &T
where
T: Clone,
{
fn f<TOut, F>(f: F, x: &T) -> TOut
where
F: Fn(T) -> TOut,
{
f(x.clone())
}
}
// This impl fails to compile:
impl<T> MyTrait<&T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: Fn(&T) -> TOut,
{
f(&x)
}
}
类型签名
impl<T> MyTrait<&T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: Fn(&T) -> TOut,
{
}
}
desugars to
impl<'a, T: 'a> MyTrait<&'a T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: for<'r> Fn(&'r T) -> TOut,
{
}
}
比特质的类型签名更一般。使用
impl<'a, T: 'a> MyTrait<&'a T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: Fn(&'a T) -> TOut,
{
}
}
将允许此编译,但将实施限制为非终止或不安全的代码。
impl<'a, T: 'a> MyTrait<&'a T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: Fn(&'a T) -> TOut,
{
//panic!(); or
f(unsafe { &*(&x as *const T) })
}
}
不安全的版本很容易在免费后引起用途,例如
println!("{:?}", String::f(|x: &String| x, "aa".to_string()));
您可以改为移动F
上的界限(游乐场(
trait MyTrait<TIn, F, TOut>
where
F: Fn(TIn) -> TOut,
{
fn f(f: F, x: Self) -> TOut;
}
impl<T, F, TOut> MyTrait<T, F, TOut> for T
where
F: Fn(T) -> TOut,
{
fn f(f: F, x: T) -> TOut {
f(x)
}
}
impl<T, F, TOut> MyTrait<T, F, TOut> for &T
where
T: Clone,
F: Fn(T) -> TOut,
{
fn f(f: F, x: &T) -> TOut {
f(x.clone())
}
}
impl<T, F, TOut> MyTrait<&T, F, TOut> for T
where
F: Fn(&T) -> TOut,
{
fn f(f: F, x: T) -> TOut {
f(&x)
}
}
我认为您的最后一个特征不会编译,因为它本质上是不安全的。
您的意义实际上等于:
impl<'a, T> MyTrait<&'a T> for T
这意味着,对于任何类型的T
和任何 lifetime 'a
,T
实现MyTrait<&'a T
>。特别是,如果'a
为'static
,则T
实现MyTrait<&'static T>
。所以我可以写这样的东西:
fn foo(x: &'static i32) -> &'static i32{
x
}
fn main() {
let sp: &'static i32 = {
<i32 as MyTrait<&'static i32>>::f(foo, 42)
};
*sp = 0; //crash!
}
(我不确定,但是我认为您什至不需要' static
来使其崩溃。我无法测试它,因为它没有编译!(。
类型系统禁止这种情况,因为特征需要:
F: Fn(TIn) -> TOut;
但是当TIn
是&T
时,实际上是:
F: for <'r> Fn(&'r TIn) -> TOut;
严格比性状更通用。
我看到的唯一方法可以安全地写这本书:
impl<T: 'static> MyTrait<&'static T> for T {
fn f<TOut, F>(f: F, x: T) -> TOut
where
F: Fn(&'static T) -> TOut,
{
f(...)
}
}
,但这可能不是您想要的,因为您不能将x
用作参数。请注意,您甚至需要制作T: 'static
,以使其完全安全。