【问题标题】:How to downcast Rc<RefCell<dyn io::Write>> into a concrete type?如何将 Rc<RefCell<dyn io::Write>> 向下转换为具体类型?
【发布时间】:2020-08-20 08:42:41
【问题描述】:

我想创建一个结构,它的文本输出既可以显示在控制台上,也可以存储在内部缓冲区中。如果文本被缓冲,那么我需要一个返回文本内容的方法。

为此,我使用了一个名为writer 的属性,即dyn std::io::Write(包装到Rc&lt;RefCell&lt;&gt;&gt; 中,因为我的真实代码需要它)。然后在结构构造中,我为此属性创建一个io::stdout() 实例或Vec::&lt;u8&gt;::new() 实例。

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

struct A {
    // Rc<RefCell<>> is needed in my real code
    writer: Rc<RefCell<dyn io::Write>>,
}

impl A {
    pub fn new() -> Self {
        Self { writer: Rc::new(RefCell::new(io::stdout())) }
    }

    pub fn new_buffered() -> Self {
        Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
    }

    pub fn write(&self, s: &str) {
        let mut writer = self.writer.borrow_mut();
        writeln!(writer, "{}", s).unwrap();
    }

    /// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
    pub fn get_buffer(&self) -> Option<String> {
        match GET_VEC_U8() { // <- Unable to implement this line
            Some(vec_u8)  => {
                Some(String::from_utf8(vec_u8.clone()).unwrap())
            },
            None => None,
        }
    }
}

fn main() {
    let a = A::new();
    a.write("foo");
    println!("Buffer: {:?}", a.get_buffer());

    let b = A::new_buffered();
    b.write("bar");
    println!("Buffer: {:?}", b.get_buffer());
}

问题

但是当作者是Vec&lt;u8&gt;时,我不知道如何提取文本内容(方法get_buffer())。我该怎么做?

我的尝试

我试图将属性包装成Box

struct A {
    writer: Rc<RefCell<Box<dyn io::Write>>>,
}

然后在上面使用Box::downcast()

impl A {
    pub fn get_buffer(&self) -> Option<String> {
        let writer = self.writer.borrow();
        match (*writer).downcast::<Vec<u8>>() {
            Ok(vec_u8) => Some(String::from_utf8(vec_u8.clone()).unwrap()),
            Err(_) => None,
        }
    }
}

但我收到此错误:

error[E0599]: no method named `downcast` found for struct `std::boxed::Box<dyn std::io::Write>` in the current scope
  --> src/main.rs:27:25
   |
27 |         match (*writer).downcast::<Vec<u8>>() {
   |                         ^^^^^^^^ method not found in `std::boxed::Box<dyn std::io::Write>`

【问题讨论】:

  • Box 上的向下转换方法仅适用于 Box&lt;dyn Any + 'static&gt;。你在这里有一个Box&lt;dyn Writer&gt;,所以你不能沮丧。将其更改为 Box&lt;dyn Writer + Any + 'static&gt; 之类的内容无济于事,因为它与 Box&lt;dyn Any + 'static&gt; 的类型不同,因此您仍然无法使用 downcast() 方法。
  • 您的选择是使用枚举而不是动态调度,或者根据Writer 使用额外的get_buffer() 方法实现自定义特征。对于Vec&lt;u8&gt;StdOut 都可以轻松实现此方法。
  • @SvenMarnach 是的,我已经尝试添加'static,确实还是不行。
  • @SvenMarnach 谢谢,您建议的自定义特征解决方案按预期工作

标签: rust dynamic-typing


【解决方案1】:

正如@SvenMarnach 在 cmets 中所写,根据io::Write 编写自定义特征可能是一种解决方案

use std::rc::Rc;
use std::cell::RefCell;
use std::io::{self, Stdout};

trait MyWrite: io::Write {
    fn get_buffer(&self) -> Option<String>;
}

impl MyWrite for Stdout {
    fn get_buffer(&self) -> Option<String> {
        None
    }
}

impl MyWrite for Vec<u8> {
    fn get_buffer(&self) -> Option<String> {
        Some(String::from_utf8(self.clone()).unwrap())
    }
}

struct A {
    // Rc<RefCell<>> is needed in my real code
    writer: Rc<RefCell<dyn MyWrite>>,
}

impl A {
    pub fn new() -> Self {
        Self { writer: Rc::new(RefCell::new(io::stdout())) }
    }

    pub fn new_buffered() -> Self {
        Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
    }

    pub fn write(&self, s: &str) {
        let mut writer = self.writer.borrow_mut();
        writeln!(writer, "{}", s).unwrap();
    }

    /// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
    pub fn get_buffer(&self) -> Option<String> {
        let writer = self.writer.borrow();
        writer.get_buffer()
    }
}

fn main() {
    let a = A::new();
    a.write("foo");
    println!("Buffer: {:?}", a.get_buffer());

    let b = A::new_buffered();
    b.write("bar");
    println!("Buffer: {:?}", b.get_buffer());
}

【讨论】:

    猜你喜欢
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多