簡體   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