如何要求 Rust 方法的泛型移动参数与对象一样长?
How to require generic moved parameters to a Rust method live as long as the object?
我正在编写一个包含 Lua 解释器的 Rust 程序,我需要将几种类型的对象保存到 Lua 管理的内存(用户数据)中,以便 Lua 将调用它们的方法。通常它们类似于 Rc<T>
,并且知道正确类型的适当方法包装器将被注册。
如果我一次只接受一种类型,我可以安全地将对象传递到 Lua 状态:
struct Foo<'a> {
badref: &'a u32,
}
struct LuaState<T> {
/* ... */
_foo: PhantomData<T>,
}
impl<T> LuaState<T> {
fn hide(&mut self, foo: T) {}
}
fn main() {
let mut l = LuaState{_foo: PhantomData};
{
let n = 7u32;
let foo = Foo { badref: &n };
/* Correctly fails to compile, since l can keep a (hidden)
* reference to n after its scope ends */
l.hide(foo);
}
}
我真的希望 LuaState
不需要类型参数,而是有一个通用的 hide
方法:
struct LuaState {
/* ... */
}
impl LuaState {
fn hide<T>(&mut self, foo: T) {}
}
...它不会为任何 T
编译,其中包含不超过 LuaState
的引用,就像我在上面一次可以为单个类型编译一样。我接近了:
trait LuaAble<'a> {}
impl<'a> LuaAble<'a> for Foo<'a> {}
struct LuaState<'a> {
marker: PhantomData<LuaAble<'a>>,
}
impl<'a> LuaState<'a> {
fn hide<T: LuaAble<'a>>(&mut self, foo: T) {}
}
这几乎可以工作;它停止了上面的悬空参考示例,但也有可能错误地实现了特征:
impl<'a, 'b> LuaAble<'b> for Foo<'a> {}
由于生命周期不再绑定在一起,这再次让隐藏的悬挂参考案例得以编译。
有什么方法可以防止按值将无法存活 &self
的东西传递给方法吗?我对任何想法都持开放态度,无论他们是否在特征、生命周期、for<'a>
、重构我的代码等方面做了一些聪明的 and/or 可怕的事情。
您可以在 LuaState<'a>
中嵌入一个假生命周期 'a
并在 hide()
中要求 T: 'a
:
struct LuaState<'a> {
_foo: PhantomData<&'a ()>,
}
impl<'a> LuaState<'a> {
fn hide<T: 'a>(&mut self, foo: T) {}
}
我不是 100% 确定这是正确的,但它似乎适用于您提供的示例。 (full playpen)
消除 PhantomData
的缺点是编译器不再理解 LuaState
在逻辑上拥有各种 T
的 Rc<T>
。如果(看起来)删除 LuaState
可能会删除 T
值,这可能会导致健全性问题。
我正在编写一个包含 Lua 解释器的 Rust 程序,我需要将几种类型的对象保存到 Lua 管理的内存(用户数据)中,以便 Lua 将调用它们的方法。通常它们类似于 Rc<T>
,并且知道正确类型的适当方法包装器将被注册。
如果我一次只接受一种类型,我可以安全地将对象传递到 Lua 状态:
struct Foo<'a> {
badref: &'a u32,
}
struct LuaState<T> {
/* ... */
_foo: PhantomData<T>,
}
impl<T> LuaState<T> {
fn hide(&mut self, foo: T) {}
}
fn main() {
let mut l = LuaState{_foo: PhantomData};
{
let n = 7u32;
let foo = Foo { badref: &n };
/* Correctly fails to compile, since l can keep a (hidden)
* reference to n after its scope ends */
l.hide(foo);
}
}
我真的希望 LuaState
不需要类型参数,而是有一个通用的 hide
方法:
struct LuaState {
/* ... */
}
impl LuaState {
fn hide<T>(&mut self, foo: T) {}
}
...它不会为任何 T
编译,其中包含不超过 LuaState
的引用,就像我在上面一次可以为单个类型编译一样。我接近了:
trait LuaAble<'a> {}
impl<'a> LuaAble<'a> for Foo<'a> {}
struct LuaState<'a> {
marker: PhantomData<LuaAble<'a>>,
}
impl<'a> LuaState<'a> {
fn hide<T: LuaAble<'a>>(&mut self, foo: T) {}
}
这几乎可以工作;它停止了上面的悬空参考示例,但也有可能错误地实现了特征:
impl<'a, 'b> LuaAble<'b> for Foo<'a> {}
由于生命周期不再绑定在一起,这再次让隐藏的悬挂参考案例得以编译。
有什么方法可以防止按值将无法存活 &self
的东西传递给方法吗?我对任何想法都持开放态度,无论他们是否在特征、生命周期、for<'a>
、重构我的代码等方面做了一些聪明的 and/or 可怕的事情。
您可以在 LuaState<'a>
中嵌入一个假生命周期 'a
并在 hide()
中要求 T: 'a
:
struct LuaState<'a> {
_foo: PhantomData<&'a ()>,
}
impl<'a> LuaState<'a> {
fn hide<T: 'a>(&mut self, foo: T) {}
}
我不是 100% 确定这是正确的,但它似乎适用于您提供的示例。 (full playpen)
消除 PhantomData
的缺点是编译器不再理解 LuaState
在逻辑上拥有各种 T
的 Rc<T>
。如果(看起来)删除 LuaState
可能会删除 T
值,这可能会导致健全性问题。