用于处理缓冲区引用的惯用Rust方法

时间:2017-08-21 11:48:27

标签: reference rust

我希望能够构造包含对可变缓冲区对象的不可变引用的对象。以下代码不起作用,但说明了我的用例,是否有一个惯用的Rust方法来处理这个?

#[derive(Debug)]
struct Parser<'a> {
    buffer: &'a String
}

fn main() {
    let mut source = String::from("Peter");
    let buffer = &source;
    let parser = Parser { buffer };

    // How can I legally change source?
    source.push_str(" Pan");

    println!("{:?}", parser);
}

3 个答案:

答案 0 :(得分:2)

生锈借用检查器的黄金法则是:一次只有一个作者或多个读者可以访问资源。这可确保算法在多个线程中安全运行。

你违反了这条规则:

#[derive(Debug)]
struct Parser<'a> {
    buffer: &'a String
}

fn main() {
    // mutable access begins here
    let mut source = String::from("Peter");
    // immutable access begins here
    let buffer = &source;
    let parser = Parser { buffer };

    source.push_str(" Pan");

    println!("{:?}", parser);
    // Both immutable and mutable access end here
}

如果您确定您的程序不能同时以可变和不可变的方式主动访问资源,您可以通过将资源包装在RefCell中来将检查从编译时间移到运行时:

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct Parser {
    buffer: Rc<RefCell<String>>
}

fn main() {
    let source = Rc::new(RefCell::new(String::from("Peter")));
    let parser = Parser { buffer: source.clone() };

    source.borrow_mut().push_str(" Pan");

    println!("{:?}", parser);
}

如果您计划在线程周围传递资源,可以使用RwLock阻止线程,直到资源可用:

use std::sync::{RwLock, Arc};

#[derive(Debug)]
struct Parser {
    buffer: Arc<RwLock<String>>
}

fn main() {
    let source = Arc::new(RwLock::new(String::from("Peter")));
    let parser = Parser { buffer: source.clone() };

    source.write().unwrap().push_str(" Pan");

    println!("{:?}", parser);
}

另一方面,您应该更喜欢&str而不是&String

答案 1 :(得分:1)

通过改变source,很难说出你想要实现的目标。我会假设你不希望它在解析器工作时发生吗?您可以随时尝试(取决于您的具体用例)将不可变的变量与额外的范围分开:

fn main() {
    let mut source = String::from("Peter");

    {    
        let buffer = &source;
        let parser = Parser { buffer };

        println!("{:?}", parser);
    }

    source.push_str(" Pan");
}

如果您不想使用RefCellunsafe(或者只是在source中保留对Parser的可变引用并使用它),我和#39;我担心它不会比普通的重构好。

答案 2 :(得分:1)

要详细说明如何不安全地完成此操作,可以通过使用原始const指针来避免借用规则来实现您所描述的内容,这当然本质上是不安全的,因为您的概念是&# 39;描述是非常不安全的。如果你选择这条道路,有办法让它更安全。但如果安全性很重要,我可能会默认使用Arc<RwLock>Arc<Mutex>

use std::fmt::{self, Display};

#[derive(Debug)]
struct Parser {
    buffer: *const String
}

impl Display for Parser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let buffer = unsafe { &*self.buffer };
        write!(f, "{}", buffer)
    }
}

fn main() {
    let mut source = String::from("Peter");
    let buffer = &source as *const String;
    let parser = Parser { buffer };

    source.push_str(" Pan");

    println!("{}", parser);
}