Strange behavior of HRTBs
我有这个代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | use std::fmt::Debug; struct S<A> where for<'a> A: Debug + 'a, { f: Box<Fn(A) -> i32>, } impl<A> S<A> where for<'a> A: Debug + 'a, { fn call(&self, a: A) { println!("Return {:?}", (self.f)(a)); } } fn create<A>(f: Box<Fn(A) -> i32>) -> S<A> where for<'a> A: Debug + 'a, { S::<A> { f } } fn helper() { let x = create::<&i32>(Box::new(|x: &i32| *x * 2)); let arg = 333; x.call(&arg); } fn main() { let x = helper(); } |
编译失败:
1 | error[E0310]: the parameter type `A` may not live long enough |
在代码 2 中,我将
1 2 3 | ... f: Box<Fn(&A) -> i32>, ... |
由于
的生命周期的影响
但是为什么代码 1 不能编译呢?
如何解决借用或非借用类型
没有简单的方法让
这就像我可以举出你的例子一样简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | struct S<A> { f: Box<Fn(A) -> i32>, } impl<A> S<A> { fn call(&self, a: A) { println!("Return {:?}", (self.f)(a)); } } fn create<A>(f: Box<Fn(A) -> i32>) -> S<A> { S { f } } fn helper() { let x = create(Box::new(|x: &i32| *x * 2)); let arg = 333; x.call(&arg); } fn main() { helper(); } |
它不起作用的原因是
请注意,如果
你今天能得到的最接近的是在一个带有生命周期参数的特征上的关联类型,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // Emulating `type Type<'a>` by moving `'a` to the trait. trait Apply<'a> { type Type; } struct Plain< T >(std::marker::PhantomData< T >); impl<'a, T> Apply<'a> for Plain< T > { type Type = T; } struct Ref<T: ?Sized>(std::marker::PhantomData< T >); impl<'a, T: ?Sized + 'a> Apply<'a> for Ref< T > { type Type = &'a T; } struct S<A: for<'a> Apply<'a>> { f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>, } impl<A: for<'a> Apply<'a>> S<A> { fn call<'a>(&self, a: <A as Apply<'a>>::Type) { println!("Return {:?}", (self.f)(a)); } } fn create<A: for<'a> Apply<'a>>( f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>, ) -> S<A> { S { f } } fn helper() { let x = create::<Ref<i32>>(Box::new(|x: &i32| *x * 2)); let arg = 333; x.call(&arg); } fn main() { helper(); } |
然而,事实证明,这种编码命中了 https://github.com/rust-lang/rust/issues/52812,所以它目前实际上不可用(而且我不知道一种解决方法)。
因为@eddyb 的workarkound 不起作用,所以我写了一个肮脏而危险的解决方法。
是的,它包含未定义的行为。但至少它在这个时候有效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | use std::fmt::Debug; struct S<A> where A: Debug + Sized, { f: Box<Fn(A) -> i32>, } impl<A> S<A> where A: Debug + Sized, { fn call< T >(&self, a: T) where T: Debug + Sized, { // assert_eq!(std::any::TypeId::of::<A>(), std::any::TypeId::of::< T >()); Not work because TypeId requires 'static lifetime // If TypeId::of supports non-static lifetime, we also need a compile-time type assert for better error message println!( "Return {:?}", unsafe { let b = std::mem::transmute::<&Box<Fn(A) -> i32>, &Box<Fn(T) -> i32>>(&self.f); let ret = b(a); std::mem::forget(b); ret } ); } } fn create<A>(f: Box<Fn(A) -> i32>) -> S<A> where for<'a> A: Debug + 'a, { S::<A> { f } } fn helper() { let x = create::<&i32>(Box::new(|x: &i32| *x * 2)); let arg = 333; x.call(&arg); x.call(&arg); x.call(&arg); } fn main() { helper(); } |
也适用于