2

我想制作一个类似于以下结构的特征(我的特定用例有点复杂,但这捕获了我遇到的问题和错误)。我遇到的问题是最后一个 impl 中有哪些生命周期。我想我需要将它们压缩到特征定义中,但我不确定如何。我怎样才能整理出生命周期以便编译?

Rust 游乐场代码链接

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)
    }
}
4

2 回答 2

1

类型签名

impl<T> MyTrait<&T> for T {
    fn f<TOut, F>(f: F, x: T) -> TOut
    where
        F: Fn(&T) -> TOut,
    {
    }
}

脱糖

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()));

您可以改为向上移动边界FPlayground

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)
    }
}
于 2019-06-27T02:45:52.283 回答
0

我认为您的最后一个特征无法编译,因为它本质上是不安全的。

您的 impl 实际上相当于:

impl<'a, T> MyTrait<&'a T> for T

这意味着,对于任何类型T任何生命周期'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这里就可以让它崩溃。我无法测试它,因为它不能编译!)。

类型系统禁止这种情况,因为 trait 需要:

F: Fn(TIn) -> TOut;

但当TIn是 a时&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用作参数。请注意,您甚至需要 make T: 'static,以使其完全安全。

于 2019-06-25T18:12:03.057 回答