Reference 处理缓冲区引用的惯用方法

Reference 处理缓冲区引用的惯用方法,reference,rust,Reference,Rust,我希望能够构造包含对可变缓冲区对象的不可变引用的对象。下面的代码不起作用,但演示了我的用例,是否有一种惯用的方法来处理这个问题 #[derive(Debug)] struct Parser<'a> { buffer: &'a String } fn main() { let mut source = String::from("Peter"); let buffer = &source; let parser = Parser { b

我希望能够构造包含对可变缓冲区对象的不可变引用的对象。下面的代码不起作用,但演示了我的用例,是否有一种惯用的方法来处理这个问题

#[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);
}
#[派生(调试)]

struct Parserrust borrow checker的黄金法则是:一次只能有一个编写器或多个读取器访问一个资源。这确保了算法可以安全地在多个线程中运行

您在此处违反此规则:

#[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
}
如果计划在线程之间传递资源,可以使用
rblock
阻止线程,直到资源可用:

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);
}
使用std::sync::{RwLock,Arc};
#[导出(调试)]
结构分析器{
缓冲区:弧
}
fn main(){
让source=Arc::new(RwLock::new(String::from(“Peter”));
让parser=parser{buffer:source.clone()};
source.write().unwrap().push_str(“Pan”);
println!(“{:?}”,解析器);
}


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

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

您在此处违反此规则:

#[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
}
如果计划在线程之间传递资源,可以使用
rblock
阻止线程,直到资源可用:

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);
}
使用std::sync::{RwLock,Arc};
#[导出(调试)]
结构分析器{
缓冲区:弧
}
fn main(){
让source=Arc::new(RwLock::new(String::from(“Peter”));
让parser=parser{buffer:source.clone()};
source.write().unwrap().push_str(“Pan”);
println!(“{:?}”,解析器);
}


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

,很难通过改变
源代码来判断你到底想要实现什么;我假设您不希望它在解析器工作时发生?您总是可以尝试(取决于您的特定用例)用一个额外的作用域来区分不可变和可变:

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

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

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

    source.push_str(" Pan");
}

如果您不想使用
RefCell
safe
(或者简单地在
解析器中保留一个对
源代码的可变引用并使用它),恐怕它不会比简单的重构更好。

很难通过对
源代码进行变异来判断您到底想要实现什么;我假设您不希望它在解析器工作时发生?您总是可以尝试(取决于您的特定用例)用一个额外的作用域来区分不可变和可变:

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

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

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

    source.push_str(" Pan");
}

如果您不想使用
RefCell
不安全的
(或者简单地在
解析器
中保留对
源代码的可变引用并使用它),恐怕它不会比简单的重构更好。

要详细说明如何不安全地完成此操作,您所描述的可以通过使用原始常量指针来避免借用规则来实现,借用规则当然是不安全的,因为您所描述的概念本身是非常不安全的。如果你选择这条路,有一些方法可以让它更安全。但如果安全性很重要,我可能会默认使用
Arc
Arc

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);
}

为了详细说明如何不安全地完成这项工作,您所描述的可以通过使用原始常量指针来避免借用规则来实现,借用规则当然本质上是不安全的,因为您所描述的概念本身是非常不安全的。如果你选择这条路,有一些方法可以让它更安全。但如果安全性很重要,我可能会默认使用
Arc
Arc

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);
}

Aa@Ijedrz指出,如果不使用内部可变性或不安全代码,这是行不通的。这两件事我都不会推荐,除非你真的有使用铁锈的经验。在95%的案例中,有更多更快、更安全的意识形态解决方案。一旦我回到家里,我可能会写一个准确的答案,以防没有其他人这样做。我能想到的唯一惯用解决方案是,在调用解析器方法时,将不可变的缓冲区引用作为参数传递给解析器,它既不使用内部可变性,也不使用不安全的代码。这让我觉得有点尴尬……另一个想法是让一个结构同时包含字符串和一个vec解析器闭包。虽然这可能需要完全重写,但它应该相当快/意识形态化。或者,如果您只需要将项目添加到缓冲区中,则可以使用一个包装器安全地编写,该包装器包含一个“UnsafeCell”,它只启用“push_str()”方法和一个getter方法,该方法返回当前长度的“str”。这两种方法都是完全安全的--sryAa@Ijedrz指出,这是在移动设备上编写的,如果不使用内部可变性或不安全代码,这是行不通的。这两件事我都不会推荐,除非你真的有使用铁锈的经验。在95%的案例中,有更多更快、更安全的意识形态解决方案。一旦我回到家里,我可能会写一个准确的答案,以防没有其他人这样做。我能想到的唯一惯用解决方案是,在调用解析器方法时,将不可变的缓冲区引用作为参数传递给解析器,它既不使用内部可变性,也不使用不安全的代码。这让我觉得有点尴尬……另一个想法是让一个结构同时包含字符串和一个vec解析器闭包。虽然这可能需要完全重写,但它应该相当快/意识形态化。或者,如果您只需要将项目添加到缓冲区中,则可以使用一个包装器安全地编写,该包装器包含一个“UnsafeCell”,它只启用“push_str()”方法和一个getter方法,该方法返回当前长度的“str”。这两种方法都是完全安全的--写在手机上,你的答案和我预期的一样