如何在Rust中获得一个简单的可变线程本地结构体?

3

我正在构建一个带有垃圾回收器的解释器。我想要一个线程本地的幼儿园区域和一个共享的老年区域。我在设置幼儿园时遇到了问题。我已经有:

const NurserySize : usize = 25000;
#[thread_local]
static mut NurseryMemory : [usize;NurserySize] = [0;NurserySize];
thread_local! {
    static Nursery: AllocableRegion = AllocableRegion::makeLocal(unsafe{&mut NurseryMemory});
}
#[cfg(test)]
mod testMemory {
    use super::*;
    #[test]
    fn test1() {
        Nursery.with(|n| n.allocObject(10));
    }
}

第一个问题是为什么需要使用unsafe - NurseryMemory是线程本地的,因此无法进行不安全的访问?

第二个问题是如何实际使用它?代码位于 playground,但它不能编译,并且我尝试修复它的尝试似乎使情况更糟。

2个回答

4

1. 为什么需要Unsafe才能获取对可变ThreadLocal的引用?

与获取普通可变静态变量的引用需要使用Unsafe相同,因为在安全代码中您可能会创建显然不正确的别名mut指针。以下代码错误地创建了两个对可变线程本地变量的可变引用:

#![feature(thread_local)]

#[thread_local]
static mut SomeValue: Result<&str, usize> = Ok("hello world");

pub fn main() {

let first = unsafe {&mut SomeValue};
let second = unsafe {&mut SomeValue};

  if let Ok(string) = first {
    *second = Err(0); // should invalidate the string reference, but it doesn't 
    println!("{}", string) // as first and second are considered to be disjunct
  } 
  
}

first即使不是可变引用,这个问题也会存在。

2. 如何修复代码?

可以使用RefCell将其放在AllocatableRegion周围,以动态强制借用内部值。

const NurserySize : usize = 25000;
#[thread_local]
static mut NurseryMemory : [usize;NurserySize] = [0;NurserySize];

thread_local! {
    static Nursery: RefCell<AllocableRegion> = RefCell::new(AllocableRegion::makeLocal(unsafe{&mut NurseryMemory}));
}

#[cfg(test)]
mod testMemory {
    use super::*;
    #[test]
    fn test1() {
        Nursery.with(|n| n.borrow_mut().allocObject(10));
    }
}

谢谢,解决了我的问题。我开始同意Rust比C更好,而不是比C++更好。也许只是因为我还没有完全理解Rust(我只写过几千行Rust代码,而在C、SML、Java和Smalltalk中我已经写了20,000多行),但对于这个应用程序来说,Rust既有帮助又有阻碍。坦率地说,如果我用我(非常纪律性)的C来做这个项目,我可能已经完成了,而在Rust中,我还没有完成25%的进度。 - Dave Mason
我不认为你的“为什么”实际上是一个“为什么”。那和let foo: Foo; let r1: = &mut foo; let r2 = &mut foo;有什么区别?我认为除了这是借用检查器的限制之外,没有其他好的理由,这个问题应该在某个时候得到解决。 - Josu Goñi

0

在Rust中,您不需要使用unsafe来创建可变的线程本地结构。但是,Nursery需要是一个RefCell。这就足够了:

use std::cell::RefCell;

const NURSERY_SIZE: usize = 300;

thread_local! {
    static NURSERY: RefCell<[usize; NURSERY_SIZE]> = RefCell::new([0; NURSERY_SIZE]);
}

#[cfg(test)]
mod test_memory {
    use super::*;
    #[test]
    fn test1() {
        NURSERY.with(|n| n.borrow_mut()[10] = 20);
    }
}

Rust Playground 链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接