我能否在不使用Box<Trait>的情况下在运行时选择Trait对象?

5
我希望在运行时(见下面的代码示例中的poly_read),能够分支并决定使用哪个Trait实现。Trait对象是在if表达式的分支中构建的,只需要在poly_read的生命周期内存在,但我需要用Box将它包装起来,因为在表达式分支内部不能从Trait借用到要赋值的绑定处。
我理解逻辑上为什么借用会过早结束,但我认为当if表达式的值被绑定时,借用检查器应该能够将借用延申至周围范围。我意识到这可能是一个天真的想法,但我想更多地了解为什么不可能。
我对现有的解决方案有点不满意,因为它需要堆分配,尽管我觉得我不应该需要这样做,因为我只在函数的生命周期内保留盒子。我想这是因为在分支被执行之前,我们不知道需要在堆栈上分配多少reader的大小,但是它不能像联合那样在编译器中表示吗,因为我们至少知道最大的大小。
顺便说一下,我实际上不知道我的担忧关于Box是否在堆上分配是有效的。一般来说,将值装箱有多么昂贵?
#![feature(io)]
#![feature(path)]

const BYTES: &'static [u8] = &[1u8, 2, 3, 4, 5];
const PATH: &'static str = "/usr/share/dict/words";

use std::old_io::{File, Reader, BufReader};


fn read(r: &mut Reader) {
    let some_bytes = r.read_exact(5).unwrap();
    assert!(some_bytes.len() == 5);
    println!("{:?}", some_bytes);
}

fn poly_read(from_file: bool) {
    // Is there any way to extend the lifetime of the ``&mut Reader`` in these branch arms without
    // boxing them as I'm doing now. It seems wasteful to do a heap allocation when the actual
    // borrow only needs to happen for body of poly_read?
    let mut reader = if from_file {
        Box::new(File::open(&Path::new(PATH)).unwrap()) as Box<Reader>
        // Would like to say:
        // File::open(&Path::new(FILE)).unwrap() as &mut Reader
    } else {
        Box::new(BufReader::new(BYTES)) as Box<Reader>
        // Would like to say:
        // BufReader::new(BYTES) as &mut Reader
    };
    // It feels like I'd like the lifetime of values returned from if expressions to be of the
    // surrounding scope, rather than the branch arms.
    read(&mut reader);
}

fn main() {
    poly_read(true);
    poly_read(false);
}

可能是重复的问题:是否允许多态变量? - Shepmaster
1
具体来说,是对那个问题的这个答案进行翻译。 - Shepmaster
是的,谢谢@Shepmaster。 - JakeK
没问题,欢迎来到 Stack Overflow!请确保给你觉得有用的问题和答案点赞。这有助于其他人(包括未来的你)找到高质量的答案。 - Shepmaster
1个回答

2
正如@Shepmaster所指出的那样,有一种类似于this answer的方法可以做到这一点,来自previous question
解决这个问题的方法是预先声明两个必要的变量:一个是File,另一个是BufReader
fn poly_read(from_file: bool) {
    // These two variables are predeclared so that they are in scope as
    // long as `reader` is
    let mut file_reader;
    let mut buf_reader;

    let mut reader = if from_file {
        file_reader = File::open(&Path::new(PATH)).unwrap();
        &mut file_reader as &mut Reader
    } else {
        buf_reader = BufReader::new(BYTES);
        &mut buf_reader as &mut Reader
    };

    read(&mut reader);
}

另请参见 Rust playpen 上的此代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接