通用特征实现中的生命周期
Lifetimes in generic trait implementation
我想制作一个类似于以下结构的特征(我的特定用例有点复杂,但这捕获了我遇到的问题和错误)。我遇到的问题是最后一个实现中的哪些生命周期。我想我需要将它们压缩到特征定义中,但我不确定如何。我如何整理生命周期以便编译?
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 实际上等同于:
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
来让它崩溃。我无法测试它,因为它无法编译!)。
类型系统禁止这种情况,因为特征要求:
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
,以使其完全安全。
类型签名
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,
{
}
}
将允许编译但将实现限制为 non-termination 或不安全代码。
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) })
}
}
不安全的版本容易造成free后再次使用,例如
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)
}
}
我想制作一个类似于以下结构的特征(我的特定用例有点复杂,但这捕获了我遇到的问题和错误)。我遇到的问题是最后一个实现中的哪些生命周期。我想我需要将它们压缩到特征定义中,但我不确定如何。我如何整理生命周期以便编译?
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 实际上等同于:
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
来让它崩溃。我无法测试它,因为它无法编译!)。
类型系统禁止这种情况,因为特征要求:
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
,以使其完全安全。
类型签名
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,
{
}
}
将允许编译但将实现限制为 non-termination 或不安全代码。
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) })
}
}
不安全的版本容易造成free后再次使用,例如
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)
}
}