我想使特征类似于以下结构(我的特定用例稍微复杂一点,但这捕获了我遇到的问题和错误)。我的问题是最后一次实现的寿命。我想我需要将它们压缩到特征定义中,但是我不确定如何。我该如何整理生命周期,以便进行编译?
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)
}
}
答案 0 :(得分: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()));
您可以改为将F
的边界向上(Playground)
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)
}
}
答案 1 :(得分:0)
我认为您的最后一个特征无法编译,因为它本质上是不安全的。
您的展示次数实际上等于:
impl<'a, T> MyTrait<&'a T> for T
这意味着,对于任何类型的T
和 any 生存期'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
,以使其完全安全。