繁体   English   中英

将Option <&mut T>转换为* mut T.

[英]Convert Option<&mut T> to *mut T

我正在围绕C库编写一个Rust包装器,同时这样做我正试图利用The Book中提到的“可空指针优化”,但我找不到将Option<&T>转换为*const TOption<&mut T>*mut T就像他们所描述的那样。

我真正想要的是能够将Some(&foo) as *const _ 不幸的是,这不起作用,所以我能想到的下一个最好的事情是Option<T>上的一个特性,它使我能够调用Some(&foo).as_ptr() 以下代码是该特征的工作定义和实现:

use std::ptr;

trait AsPtr<T> {
    fn as_ptr(&self) -> *const T;
}

impl<'a, T> AsPtr<T> for Option<&'a T> {
    fn as_ptr(&self) -> *const T {
        match *self {
            Some(val) => val as *const _,
            None => ptr::null(),
        }
    }
}

现在我可以调用Some(&foo).as_ptr()来获取*const _ ,我希望能够调用Some(&mut foo).as_ptr()来获得*mut _ 以下是我为此做的新特征:

trait AsMutPtr<T> {
    fn as_mut_ptr(&self) -> *mut T;
}

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> {
    fn as_mut_ptr(&self) -> *mut T {
        match *self {
            Some(val) => val as *mut _,
            None => ptr::null_mut(),
        }
    }
}

问题是, AsMutPtr特性不会编译。 当我尝试时,我收到以下错误:

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:22:15
   |
22 |         match *self {
   |               ^^^^^
   |               |
   |               cannot move out of borrowed content
   |               help: consider removing the `*`: `self`
23 |             Some(val) => val as *mut _,
   |                  --- data moved here
   |
note: move occurs because `val` has type `&mut T`, which does not implement the `Copy` trait
  --> src/lib.rs:23:18
   |
23 |             Some(val) => val as *mut _,
   |                  ^^^

我没有看到导致它失败的两个特征之间发生了什么变化 - 我不认为添加mut会产生那么大的差异。 我尝试添加一个ref ,但这只会导致一个不同的错误,我不希望反正需要它。

为什么AsMutPtr特性不起作用?

不幸的是,为&mut T而不是&T编写特征impl 产生很大的不同。 &mut T&T相反,不是Copy ,因此您无法直接从共享引用中提取它:

& &T      --->  &T
& &mut T  -/->  &mut T

这是相当自然的 - 否则可能会出现可变引用的别名,这违反了Rust的借用规则。

你可能会问外在&来自哪里。 它实际上来自as_mut_ptr()方法中的&self 如果你对某些东西有一个不可变的引用,即使它内部包含可变引用,你也无法使用它们来改变它们背后的数据。 这也违反了借用语义。

不幸的是,我认为没有办法做到这一点没有不安全。 你需要有&mut T “by value”才能将它转换为*mut T ,但你不能通过共享引用“按值”获取它。 因此,我建议你使用ptr::read()

use std::ptr;

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> {
    fn as_mut_ptr(&self) -> *mut T {
        match *self {
            Some(ref val) => unsafe { ptr::read(val) as *mut _ },
            None => ptr::null_mut(),
        }
    }
}

这里的val& &mut T因为模式中有ref限定符,因此ptr::read(val)返回&mut T ,别名为mutable引用。 我认为它可以立即转换为原始指针并且不会泄漏,但即使结果是原始指针,它仍然意味着你有两个别名可变指针。 你应该非常小心你用它们做什么。

或者,您可以修改AsMutPtr::as_mut_ptr()以按值使用其目标:

trait AsMutPtr<T> {
    fn as_mut_ptr(self) -> *mut T;
}

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> {
    fn as_mut_ptr(self) -> *mut T {
        match self {
            Some(value) => value as *mut T,
            None => ptr::null_mut()
        }
    }
}

但是,在这种情况下, Option<&mut T>将由as_mut_ptr() 例如,如果此Option<&mut T>存储在结构中,则这可能是不可行的。 我不确定是否有可能以某种方式使用Option<&mut T>手动执行重新借用而不仅仅是&mut T (它不会自动触发); 如果可能,那么按值as_mut_ptr()可能是最好的整体解决方案。

问题是,你正在阅读的&mut出的& ,但&mut s的不Copy所以必须移动-你不能搬出一个const引用。 这实际上解释了Vladimir Matveev在更基本属性方面对&&mut → & s的洞察力。

这实际上相对简单地解决了。 如果你能读一个*const _ ,你可以读一个*mut _ 这两个是相同的类型,标志着“小心,这是共享的”。 由于解除引用无论如何都是不安全的,实际上没有理由阻止你在两者之间施放。

所以你可以做到

match *self {
    Some(ref val) => val as *const _ as *mut _,
    None => ptr::null_mut(),
}

读取不可变引用,使其成为不可变指针,然后使其成为可变指针。 此外,它都是通过安全的Rust完成的,所以我们知道我们没有违反任何别名规则。

也就是说,在&mut引用消失之前实际使用那个*mut指针可能是一个非常糟糕的主意。 我对此非常犹豫,并试图重新考虑你的包装更安全。

这会做你期望的吗?

trait AsMutPtr<T> {
    fn as_mut_ptr(self) -> *mut T;
}

impl<T> AsMutPtr<T> for Option<*mut T> {
    fn as_mut_ptr(self) -> *mut T {
        match self {
            Some(val) => val as *mut _,
            None => ptr::null_mut(),
        }
    }
}

为避免unsafe代码,请将特征更改为accept &mut self而不是self&self

trait AsMutPtr<T> {
    fn as_mut_ptr(&mut self) -> *mut T;
}

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> {
    fn as_mut_ptr(&mut self) -> *mut T {
        match self {
            Some(v) => *v,
            None => ptr::null_mut(),
        }
    }
}

如果您感觉如此,您还可以将实现减少到一行:

fn as_mut_ptr(&mut self) -> *mut T {
    self.as_mut().map_or_else(ptr::null_mut, |v| *v)
}

这可用于为您提供来自同一源的多个可变原始指针。 这很容易导致您导致可变别名 ,所以要小心:

fn example(mut v: Option<&mut u8>) {
    let b = v.as_mut_ptr();
    let a = v.as_mut_ptr();
}

我建议不要将不可变引用转换为可变指针,因为这可能导致未定义的行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM