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”。这两种方法都是完全安全的--写在手机上,你的答案和我预期的一样