共享在 Rust 中实现 Trait 的对象

问题描述 投票:0回答:1

我有一个可以从某个索引提供字节的对象的特征。这些可能是文件、正在跟踪的进程、其他字节提供程序上的缓存等:

use std::result::Result;
use std::io::Error;

trait ByteProvider {
    fn provide_bytes(&mut self, index: usize, dest: &mut[u8]) -> Result<usize, Error>;
}

struct ZeroProvider { }

impl ByteProvider for ZeroProvider {
    fn provide_bytes(&mut self, _index: usize, dest: &mut[u8]) -> Result<usize, Error> {
        dest.iter_mut().for_each(|e| *e = 0);
        Ok(dest.len())
    }
}

我还有一组小部件,可用于实现 ByteProvider 特征的任何泛型类型:

struct HexDump<T: ByteProvider> {
    provider: T
}

struct Disassembler<T: ByteProvider> {
    provider: T
}

impl<T: ByteProvider> HexDump<T> {
    pub fn new(provider: T) -> Self { Self { provider } }
    pub fn dump(&mut self) {
        let mut bytes = [0; 16];
        self.provider.provide_bytes(0, &mut bytes).unwrap();
        println!("{}", bytes.iter().map(|e| format!("{:02x}", e)).collect::<Vec<String>>().join(" "));
    }
}

impl<T: ByteProvider> Disassembler<T> {
    pub fn new(provider: T) -> Self { Self { provider } }
    pub fn disassemble(&mut self) {
        println!("Disassembly");
    }
}

这很好用:

fn main() {
    let provider = ZeroProvider {};
    let mut dumper = HexDump::new(provider);
    dumper.dump();
}

...但是,我希望对同一数据有多个视图:

fn main() {
    let provider = ZeroProvider {};
    let mut dumper = HexDump::new(provider);
    let mut disassembler = Disassembler::new(provider);
    dumper.dump();
    disassembler.disassemble();
}

但是这当然不是有效的 Rust,所以我转向引用计数智能指针来寻求帮助,期望它能够工作:

use std::rc::Rc;
fn main() {
    let provider = Rc::new(ZeroProvider {});
    let mut dumper = HexDump::new(Rc::clone(&provider));
    let mut disassembler = Disassembler::new(Rc::clone(&provider));
    dumper.dump();
    disassembler.disassemble();
}

但是,编译器不喜欢这样:

27  | impl<T: ByteProvider> HexDump<T> {
    |         ^^^^^^^^^^^^  ----------
    |         |
    |         unsatisfied trait bound introduced here
...
error[E0599]: the method `dump` exists for struct `HexDump<Rc<_, _>>`, but its trait bounds were not satisfied
   --> src/main.rs:48:12
    |
19  |   struct HexDump<T: ByteProvider> {
    |   ------------------------------- method `dump` not found for this struct
...
48  |       dumper.dump();
    |              ^^^^ method cannot be called on `HexDump<Rc<_, _>>` due to unsatisfied trait bounds

...反汇编程序有类似的错误,我无法破译。我错过了什么?

我认为问题是 Rc 没有实现我的特征,但它没有公开其嵌套对象接口吗?

我希望小部件(HexDump 和反汇编器)不要知道它们的字节提供程序正在被共享(如果可能的话)。这能实现吗?

rust traits sharing reference-counting
1个回答
0
投票

如评论中所述,预期用途意味着

ByteProvider
是在给定时间使用的多个小部件之间共享
这意味着
.provide_bytes()
函数应该使用 shared 引用 (
&self
) 而不是 exclusive 引用 (
&mut self
)。
然后,每个小部件都可以保留对此 ByteProvider
shared
引用(而不是拥有它)。
当然,
ByteProvider
必须比小部件更长寿。

对于

ZeroProvider
示例,没有问题,因为它没有数据成员,所以没有什么可以改变。
另一方面,如果我们有一个具有内部状态的提供者,在使用时需要改变,那么
&self
就会出现问题。
这就是我们需要内部可变性:这个想法是在运行时而不是编译时检查独占访问。
如下面的
CountProvider
示例所示,内部状态(为简洁起见,这里使用一个简单的整数)嵌入到
RefCell
中,以便仅在需要时可变地借用此内部状态(多线程上下文中的等效项是
RwLock
)。

trait ByteProvider {
    fn provide_bytes(
        &self,
        index: usize,
        dest: &mut [u8],
    ) -> Result<usize, Box<dyn std::error::Error>>;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

struct ZeroProvider {}
impl ByteProvider for ZeroProvider {
    fn provide_bytes(
        &self,
        _index: usize,
        dest: &mut [u8],
    ) -> Result<usize, Box<dyn std::error::Error>> {
        dest.iter_mut().for_each(|e| *e = 0);
        Ok(dest.len())
    }
}

struct CountProvider {
    count: std::cell::RefCell<u8>,
}
impl CountProvider {
    fn new() -> Self {
        Self {
            count: std::cell::RefCell::new(0),
        }
    }
}
impl ByteProvider for CountProvider {
    fn provide_bytes(
        &self,
        _index: usize,
        dest: &mut [u8],
    ) -> Result<usize, Box<dyn std::error::Error>> {
        let mut count = self.count.borrow_mut();
        dest.iter_mut().for_each(|e| {
            *e = *count;
            *count += 1;
        });
        Ok(dest.len())
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

struct HexDump<'a, T: ByteProvider> {
    provider: &'a T,
}

impl<'a, T: ByteProvider> HexDump<'a, T> {
    pub fn new(provider: &'a T) -> Self {
        Self { provider }
    }
    pub fn dump(&mut self) {
        let mut bytes = [0; 16];
        self.provider.provide_bytes(0, &mut bytes).unwrap();
        println!(
            "Dumping {}",
            bytes
                .iter()
                .map(|e| format!("{:02x}", e))
                .collect::<Vec<String>>()
                .join(" ")
        );
    }
}

struct Disassembler<'a, T: ByteProvider> {
    provider: &'a T,
}

impl<'a, T: ByteProvider> Disassembler<'a, T> {
    pub fn new(provider: &'a T) -> Self {
        Self { provider }
    }
    pub fn disassemble(&mut self) {
        let mut bytes = [0; 16];
        self.provider.provide_bytes(0, &mut bytes).unwrap();
        println!("Disassembling {:?}", bytes);
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

fn main() {
    let provider = ZeroProvider {};
    let mut dumper = HexDump::new(&provider);
    let mut disassembler = Disassembler::new(&provider);
    dumper.dump();
    disassembler.disassemble();
    //
    let provider = CountProvider::new();
    let mut dumper = HexDump::new(&provider);
    let mut disassembler = Disassembler::new(&provider);
    dumper.dump();
    disassembler.disassemble();
}
/*
Dumping 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Disassembling [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Dumping 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
Disassembling [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
*/
© www.soinside.com 2019 - 2024. All rights reserved.