我有一个对象的trait,可以从一些索引中提供字节.这些可以是文件、被ptraced的进程、其他字节提供程序上的缓存等:

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和反汇编程序)不要意识到它们的字节提供程序是共享的.这能做到吗?

推荐答案

As stated in the comments, the intended usage requires the ByteProvider to be shared between the multiples widgets which are in use at a given time.
This implies that the .provide_bytes() function should use a shared reference (&self) instead of an exclusive reference (&mut self); I discovered here that the shared/exclusive aspect should be considered first in the design, then the constant/mutable aspect is only a consequence of this choice.
Then, each widget can keep a shared reference to this ByteProvider (instead of owning it).
Of course, the ByteProvider must outlive the widgets; if we cannot guaranty that statically, then Rc<T> should be used instead of &T.

With the ZeroProvider example, there is no problem since it has no data members, then nothing to be mutated.
On the other hand, if we have a provider with an internal state which needs to be mutated when used, the &self would be problematic.
That's here that we need interior mutability: the idea is to check the exclusive access at runtime instead of compile time.
As shown on the CountProvider example below, the internal state (a simple integer here for brevity) is embedded in a RefCell in order to mutably borrow this internal state only when needed (the equivalent in a multithreaded context would be 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> {
    provider: &'a T,
}
impl<'a, T: ByteProvider> HexDump<'a, T> {
    pub fn new(provider: &'a T) -> Self {
        Self { provider }
    }
    pub fn dump(&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> {
    provider: &'a T,
}
impl<'a, T: ByteProvider> Disassembler<'a, T> {
    pub fn new(provider: &'a T) -> Self {
        Self { provider }
    }
    pub fn disassemble(&self) {
        let mut bytes = [0; 16];
        self.provider.provide_bytes(0, &mut bytes).unwrap();
        println!("Disassembling {:?}", bytes);
    }
}

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

fn main() {
    let provider = ZeroProvider {};
    let dumper = HexDump::new(&provider);
    let disassembler = Disassembler::new(&provider);
    dumper.dump();
    disassembler.disassemble();
    //
    let provider = CountProvider::new();
    let dumper = HexDump::new(&provider);
    let 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]
*/

Rust相关问答推荐

有条件默认实现

基于对vec值的引用从该值中删除该值

泛型属性比较

在特征中使用Async时,如何解决不透明类型`impl Future<;out=self>;`不满足其关联的类型边界和警告?

值为可变对象的不可变HashMap

在跨平台应用程序中使用std::OS::Linux和std::OS::Windows

在铁 rust 中传递所有权

什么时候和为什么S最好是按值或引用传递简单类型

无法定义名为&new&的关联函数,该函数的第一个参数不是self

通过RabbitMQ取消铁 rust 中长时间运行的人造丝任务的策略

如何在AVX2中对齐/旋转256位向量?

不同类型泛型的映射

用于实现获取 struct 体 id 的特征规范

如何使用tracing-subscriberRust crate 构建多编写者、全局过滤订阅者

我如何取消转义,在 Rust 中多次转义的字符串?

从嵌入式 Rust 中的某个时刻开始经过的时间

如何刷新 TcpStream

当用作函数参数时,不强制执行与绑定的关联类型

如何在 Rust 的内置函数上实现特征?

您不能borrow 对只读值的可变引用