如何将借用值包装在也是借用值的 newtype 中? [英] How to wrap a borrowed value in a newtype that is also a borrowed value?

查看:44
本文介绍了如何将借用值包装在也是借用值的 newtype 中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 newtype 模式 包装一个预先存在的类型.该内部类型有一个 modify 方法,它允许我们在回调中使用借用的可变值:

I am trying to use the newtype pattern to wrap a pre-existing type. That inner type has a modify method which lets us work with a borrowed mutable value in a callback:

struct Val;

struct Inner(Val);

impl Inner {
    fn modify<F>(&self, f: F)
    where F: FnOnce(&mut Val) -> &mut Val { … }
}

现在我想在我的 newtype Outer 上提供一个非常相似的方法,但是它不应该在 Val s 上工作,但又是一个 newtype 包装器 WrappedVal:

Now I want to provide a very similar method on my newtype Outer, which however should not work on Vals but again a newtype wrapper 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 因为它使用内部可变性 - 它是一种表示嵌入式系统上的外围寄存器的类型

This code is a reduced example from the original API. I don't know why the reference is returned from the closure, maybe to facilitate chaining, but it shouldn't be necessary. It takes &self because it uses internal mutability - it's a type representing a peripheral register on an embedded system

如何从 &mut Val 获取 &mut WrappedVal?

How do I get a &mut WrappedVal from a &mut Val?

我尝试了各种方法,但都被借用检查器破坏了.我无法将 Val 移出可变引用以构建正确的 WrappedVal,并且在尝试使用 struct WrappedVal(&'? mut Val)(实际上我并不想要,因为它们使 trait 实现复杂化).

I have tried various things, but all were busted by the borrow-checker. I cannot move the Val out of the mutable reference to construct a proper WrappedVal, and I couldn't get lifetimes to compile either when experimenting around with struct WrappedVal(&'? mut Val) (which I don't really want actually, since they are complicating a trait implementation).

我最终让它编译(见 Rust playground 演示) 使用绝对恐怖的

I eventually got it to compile (see Rust playground demo) using the absolute horror of

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()
});

但肯定有更好的方法吗?

but surely there must be a better way?

推荐答案

您当前的定义没有安全的方法,并且您的不安全代码不能保证是安全的.没有约定 WrappedVal 的布局与 Val 的布局相匹配,即使这就是它所拥有的全部内容.

There is no safe way with your current definition, and your unsafe code is not guaranteed to be safe. There's no contract that the layout of a WrappedVal matches that of a Val, even though that's all it holds.

别这样.相反,包装参考:

Don't do it. Instead, wrap the reference:

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) 后,两个指针可以互换.我运行了 对 Miri 和您的完整示例并且没有收到任何错误,但这并不是我没有搞砸其他事情的灵丹妙药.

With repr(transparent) in place, the two pointers are interchangable. I ran a quick test with Miri and your full example and didn't receive any errors, but that's not a silver bullet that I didn't mess something else up.

这篇关于如何将借用值包装在也是借用值的 newtype 中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆