简体   繁体   English

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

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

I'm writing a Rust wrapper around a C library and while doing so I'm trying to take advantage of the "nullable pointer optimization" mentioned in The Book , but I can't find a good way to convert Option<&T> to *const T and Option<&mut T> to *mut T like what they're describing. 我正在围绕C库编写一个Rust包装器,同时这样做我正试图利用The Book中提到的“可空指针优化”,但我找不到将Option<&T>转换为*const TOption<&mut T>*mut T就像他们所描述的那样。

What I really want is to be able to call Some(&foo) as *const _ . 我真正想要的是能够将Some(&foo) as *const _ Unfortunately that doesn't work, so the next best thing I can think of is a trait on Option<T> that enables me to call Some(&foo).as_ptr() . 不幸的是,这不起作用,所以我能想到的下一个最好的事情是Option<T>上的一个特性,它使我能够调用Some(&foo).as_ptr() The following code is a working definition and implementation for that trait: 以下代码是该特征的工作定义和实现:

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

Now that I can call Some(&foo).as_ptr() to get a *const _ , I want to be able to call Some(&mut foo).as_ptr() to get a *mut _ . 现在我可以调用Some(&foo).as_ptr()来获取*const _ ,我希望能够调用Some(&mut foo).as_ptr()来获得*mut _ The following is the new trait I made to do this: 以下是我为此做的新特征:

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

The problem is, the AsMutPtr trait won't compile. 问题是, AsMutPtr特性不会编译。 When I try, I get the following error: 当我尝试时,我收到以下错误:

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 _,
   |                  ^^^

I don't see what changed between the two traits that causes it to fail — I didn't think adding mut would make that big a difference. 我没有看到导致它失败的两个特征之间发生了什么变化 - 我不认为添加mut会产生那么大的差异。 I tried adding a ref , but that just causes a different error, and I wouldn't expect to need that anyway. 我尝试添加一个ref ,但这只会导致一个不同的错误,我不希望反正需要它。

Why doesn't the AsMutPtr trait work? 为什么AsMutPtr特性不起作用?

Unfortunately, writing the trait impl for &mut T instead of &T does make a big difference. 不幸的是,为&mut T而不是&T编写特征impl 产生很大的不同。 &mut T , as opposed to &T , is not Copy , therefore you cannot extract it out of a shared reference directly: &mut T&T相反,不是Copy ,因此您无法直接从共享引用中提取它:

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

This is fairly natural - otherwise aliasing of mutable references would be possible, which violates Rust borrowing rules. 这是相当自然的 - 否则可能会出现可变引用的别名,这违反了Rust的借用规则。

You may ask where that outer & comes from. 你可能会问外在&来自哪里。 It actually comes from &self in as_mut_ptr() method. 它实际上来自as_mut_ptr()方法中的&self If you have an immutable reference to something, even if that something contains mutable references inside it, you won't be able to use them to mutate the data behind them. 如果你对某些东西有一个不可变的引用,即使它内部包含可变引用,你也无法使用它们来改变它们背后的数据。 This also would be a violation of borrowing semantics. 这也违反了借用语义。

Unfortunately, I see no way to do this without unsafe. 不幸的是,我认为没有办法做到这一点没有不安全。 You need to have &mut T "by value" in order to cast it to *mut T , but you can't get it "by value" through a shared reference. 你需要有&mut T “by value”才能将它转换为*mut T ,但你不能通过共享引用“按值”获取它。 Therefore, I suggest you to use ptr::read() : 因此,我建议你使用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 here is & &mut T because of ref qualifier in the pattern, therefore ptr::read(val) returns &mut T , aliasing the mutable reference. 这里的val& &mut T因为模式中有ref限定符,因此ptr::read(val)返回&mut T ,别名为mutable引用。 I think it is okay if it gets converted to a raw pointer immediately and does not leak out, but even though the result would be a raw pointer, it still means that you have two aliased mutable pointers. 我认为它可以立即转换为原始指针并且不会泄漏,但即使结果是原始指针,它仍然意味着你有两个别名可变指针。 You should be very careful with what you do with them. 你应该非常小心你用它们做什么。

Alternatively, you may modify AsMutPtr::as_mut_ptr() to consume its target by value: 或者,您可以修改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()
        }
    }
}

However, in this case Option<&mut T> will be consumed by as_mut_ptr() . 但是,在这种情况下, Option<&mut T>将由as_mut_ptr() This may not be feasible if, for example, this Option<&mut T> is stored in a structure. 例如,如果此Option<&mut T>存储在结构中,则这可能是不可行的。 I'm not really sure whether it is possible to somehow perform reborrowing manually with Option<&mut T> as opposed to just &mut T (it won't be triggered automatically); 我不确定是否有可能以某种方式使用Option<&mut T>手动执行重新借用而不仅仅是&mut T (它不会自动触发); if it is possible, then by-value as_mut_ptr() is probably the best overall solution. 如果可能,那么按值as_mut_ptr()可能是最好的整体解决方案。

The problem is that you are reading an &mut out of an & , but &mut s are not Copy so must be moved - and you can't move out of a const reference. 问题是,你正在阅读的&mut出的& ,但&mut s的不Copy所以必须移动-你不能搬出一个const引用。 This actually explains Vladimir Matveev insight about &&mut → & s in terms of more fundamental properties. 这实际上解释了Vladimir Matveev在更基本属性方面对&&mut → & s的洞察力。

This is actually relatively simply solved. 这实际上相对简单地解决了。 If you can read a *const _ , you can read a *mut _ . 如果你能读一个*const _ ,你可以读一个*mut _ The two are the same type, bar a flag that says "be careful, this is being shared". 这两个是相同的类型,标志着“小心,这是共享的”。 Since dereferences are unsafe either way, there's actually no reason to stop you casting between the two. 由于解除引用无论如何都是不安全的,实际上没有理由阻止你在两者之间施放。

So you can actually do 所以你可以做到

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

Read the immutable reference, make it an immutable pointer and then make it a mutable pointer. 读取不可变引用,使其成为不可变指针,然后使其成为可变指针。 Plus it's all done through safe Rust so we know we're not breaking any aliasing rules. 此外,它都是通过安全的Rust完成的,所以我们知道我们没有违反任何别名规则。

That said, it's probably a really bad idea to actually use that *mut pointer until the &mut reference is gone. 也就是说,在&mut引用消失之前实际使用那个*mut指针可能是一个非常糟糕的主意。 I would be very hesitant with this, and try to rethink your wrapper to something safer. 我对此非常犹豫,并试图重新考虑你的包装更安全。

Will this do what you expect? 这会做你期望的吗?

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

To avoid unsafe code, change the trait to accept &mut self instead of either self or &self : 为避免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(),
        }
    }
}

You could also reduce the implementation to one line, if you felt like it: 如果您感觉如此,您还可以将实现减少到一行:

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

This can be used to give you multiple mutable raw pointers from the same source. 这可用于为您提供来自同一源的多个可变原始指针。 This can easily lead you to causing mutable aliasing , so be careful: 这很容易导致您导致可变别名 ,所以要小心:

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

I would recommend not converting the immutable reference to a mutable pointer as this is very likely to cause undefined behavior. 我建议不要将不可变引用转换为可变指针,因为这可能导致未定义的行为。

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

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