如何将借来的值包装在也是借来的值的新类型中?
How to wrap a borrowed value in a newtype that is also a borrowed value?
我正在尝试使用 newtype pattern 包装一个预先存在的类型。该内部类型有一个 modify
方法,可以让我们在回调中使用借用的可变值:
struct Val;
struct Inner(Val);
impl Inner {
fn modify<F>(&self, f: F)
where F: FnOnce(&mut Val) -> &mut Val { … }
}
现在我想在我的新类型 Outer
上提供一个非常相似的方法,但是它不应该在 Val
s 上工作,但又是一个新类型包装器 WrappedVal
:
struct Outer(Inner);
struct WrappedVal(Val);
impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
{
self.0.modify(|v| f(/* ??? */));
}
}
此代码是原始 API 的简化示例。我不知道为什么从闭包中返回引用,也许是为了方便链接,但这不是必需的。它需要 &self
因为它使用内部可变性——它是一种表示嵌入式系统上外围寄存器的类型
如何从 &mut Val
中获取 &mut WrappedVal
?
我尝试过各种方法,但都被借用检查器破坏了。我无法将 Val
从可变引用中移出以构建适当的 WrappedVal
,并且在尝试使用 struct WrappedVal(&'? mut Val)
时我也无法编译生命周期(我真的不知道实际上想要,因为它们使特征实现复杂化。
我最终使用
的绝对恐怖来编译它(参见 Rust playground demo)
self.0.modify(|v| unsafe {
(f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
.as_mut()
.unwrap()
});
但一定有更好的方法吧?
您当前的定义没有安全方法,您的不安全代码不保证运行安全。没有合同规定 WrappedVal
的布局与 Val
的布局匹配,即使仅此而已。
解决方案不使用unsafe
不要这样做。相反,包装参考:
struct WrappedVal<'a>(&'a mut Val);
impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(WrappedVal) -> WrappedVal,
{
self.0.modify(|v| f(WrappedVal(v)).0)
}
}
解决方案使用 unsafe
您可以声明您的类型与它包装的类型具有相同的 表示,通过 repr(transparent)
:
使指针兼容
#[repr(transparent)]
struct WrappedVal(given::Val);
impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
{
self.0.modify(|v| {
// Insert documentation why **you** think this is safe
// instead of copy-pasting from Stack Overflow
let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
let wv = f(wv);
unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
})
}
}
有了repr(transparent)
,两个指针就可以互换了。我 运行 a quick test with Miri and your full example 并没有收到任何错误,但这不是我没有搞砸其他事情的灵丹妙药。
使用 ref_cast 库你可以写:
#[derive(RefCast)]
#[repr(transparent)]
struct WrappedVal(Val);
然后你可以使用WrappedVal::ref_cast_mut(v)
进行转换。
我正在尝试使用 newtype pattern 包装一个预先存在的类型。该内部类型有一个 modify
方法,可以让我们在回调中使用借用的可变值:
struct Val;
struct Inner(Val);
impl Inner {
fn modify<F>(&self, f: F)
where F: FnOnce(&mut Val) -> &mut Val { … }
}
现在我想在我的新类型 Outer
上提供一个非常相似的方法,但是它不应该在 Val
s 上工作,但又是一个新类型包装器 WrappedVal
:
struct Outer(Inner);
struct WrappedVal(Val);
impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
{
self.0.modify(|v| f(/* ??? */));
}
}
此代码是原始 API 的简化示例。我不知道为什么从闭包中返回引用,也许是为了方便链接,但这不是必需的。它需要 &self
因为它使用内部可变性——它是一种表示嵌入式系统上外围寄存器的类型
如何从 &mut Val
中获取 &mut WrappedVal
?
我尝试过各种方法,但都被借用检查器破坏了。我无法将 Val
从可变引用中移出以构建适当的 WrappedVal
,并且在尝试使用 struct WrappedVal(&'? mut Val)
时我也无法编译生命周期(我真的不知道实际上想要,因为它们使特征实现复杂化。
我最终使用
的绝对恐怖来编译它(参见 Rust playground demo)self.0.modify(|v| unsafe {
(f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
.as_mut()
.unwrap()
});
但一定有更好的方法吧?
您当前的定义没有安全方法,您的不安全代码不保证运行安全。没有合同规定 WrappedVal
的布局与 Val
的布局匹配,即使仅此而已。
解决方案不使用unsafe
不要这样做。相反,包装参考:
struct WrappedVal<'a>(&'a mut Val);
impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(WrappedVal) -> WrappedVal,
{
self.0.modify(|v| f(WrappedVal(v)).0)
}
}
解决方案使用 unsafe
您可以声明您的类型与它包装的类型具有相同的 表示,通过 repr(transparent)
:
#[repr(transparent)]
struct WrappedVal(given::Val);
impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
{
self.0.modify(|v| {
// Insert documentation why **you** think this is safe
// instead of copy-pasting from Stack Overflow
let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
let wv = f(wv);
unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
})
}
}
有了repr(transparent)
,两个指针就可以互换了。我 运行 a quick test with Miri and your full example 并没有收到任何错误,但这不是我没有搞砸其他事情的灵丹妙药。
使用 ref_cast 库你可以写:
#[derive(RefCast)]
#[repr(transparent)]
struct WrappedVal(Val);
然后你可以使用WrappedVal::ref_cast_mut(v)
进行转换。