簡體   English   中英

Rust:生命周期使用不當

[英]Rust: Bad use of lifetime

我用生命周期測試了一個小例子,但有時我無法理解這個問題,就像這里:

struct Space<'r> {
    v: &'r mut usize,
    z: usize,
}

impl<'r> Space<'r> {
    fn new(v: &'r mut usize) -> Self {
        Self { v, z: 0 }
    }
    
    fn add(&'r mut self) {
        self.z += 1;
        *self.v = self.z
    }

    fn make_val(&'r mut self, v: u32) -> Val<'r> {
        Val::new(self, v)
    }
}

struct Val<'r> {
    space: &'r mut Space<'r>,
    v: u32,
}

impl<'r> Val<'r> {
    fn new(space: &'r mut Space<'r>, v: u32) -> Self {
        Self { space, v }
    }
}

impl<'r> Clone for Val<'r> {
    fn clone(&self) -> Self {
        self.space.add();
        
        Self { space: self.space, v: self.v }
    }
}

fn main() {
    let mut v = 0usize;
    let mut space = Space::new(&mut v);
    let mut val1 = space.make_val(1234);
    let mut val2 = val1.clone();
    
    println!("Res: {} - {}/{}", v, val1.v, val2.v);
}

游樂場

有 2 個編譯錯誤,第 36 行的錯誤對我來說很難修復,但第 34 行的第一個錯誤真的很奇怪,它說生命周期不能超過,因為方法調用是內部的,什么都沒有。

你能解釋一下這些問題以及如何解決它們嗎?

通過對我來說突出的部分,以下是我可能會做出的改變。

結構

通過您擁有的兩個結構,第一個看起來很合理。

struct Space<'r> {
    v: &'r mut usize,
    z: usize,
}

但是, Val結構對我來說沒有多大意義。 如果參照Space具有相同的壽命'rSpace本身,那么它是本質上等價於移動Val<'r>Space<'r> 雖然不違法,但我建議更改它。

// Just claim Space<'r> completely
struct Val<'r> {
    space: Space<'r>,
    v: u32,
}

另一種選擇是為Val<'r>添加一個額外的生命周期,這樣make_val可以被多次調用而不會導致生命周期沖突。

// life of reference to space 'a <= 'r
struct Val<'a, 'r: 'a> {
    space: &'a mut Space<'r>,
    v: u32,
}

// Modify to allow for second lifetime to be used.
fn make_val<'a>(&'a mut self, v: u32) -> Val<'a, 'r> {
    Val { space: self, v }
}

Space::add

&'r mut self上的顯式生命周期對於函數運行來說不是必需的。 在嘗試運行代碼時遇到的兩個錯誤中,整個第一個錯誤只是編譯器告訴您為什么這不起作用。 它過度限制了函數並不必要地混淆了編譯器,因此讓我們將其刪除。

fn add(&mut self) {
    self.z += 1;
    *self.v = self.z
}

它無法解決它的原因是因為&mut self實際上是您要求平等的兩個生命周期。 如果我們可視化所有涉及的生命周期,可能會更有意義:

// add(&mut self) tells the compiler to infer a lifetime for 'a that doesn't exceed 'b
fn add<'a, 'b: 'a>(self: &'a mut Space<'b>) {}

// add(&'r mut self) tells the compiler that the reference must live as long as the data 
fn add<'b>(self: &'b mut Space<'b>) {}

克隆

克隆包含可變引用的內容沒有意義。 Rust 非常明確地禁止在內存中的任何給定點有多個可變引用。 克隆引用在技術上是可行的,但需要不安全的代碼。 你永遠不應該這樣做,但我還是在下面寫了它。

impl<'r> Clone for Val<'r> {
    fn clone(&self) -> Val<'r> {
        unsafe {
            // Make self mutable so we can access self.space
            let my_self: &mut Self = &mut *(self as *const _ as *mut _);

            // Requires a mutable reference to self to run space::add
            my_self.space.add();
        
            Self {
                // Duplicate reference via unsafe code
                space: &mut *my_self.space,
                v: self.v
            }
        }
    }
}

印刷

最后,main 中的打印行使用v盡管對它的可變引用仍在使用中。 這是相當不言自明的,所以我不會詳細介紹。

安全防銹

解決這個問題的安全方法是使用Rc<Refcell<T>> Rc允許跨單個線程獲取多個擁有的引用,而RefCell允許內部可變性。 RefCell能夠通過檢查在運行時而不是編譯時是否存在任何其他引用來做到這一點。 這是一個使用單個RefCell操場版本,但也可以使用第二個來更好地匹配您的原始代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM