我可以在运行时选择特质对象而不使用Box< Trait>吗? [英] Can I select a trait object at runtime without using a Box<Trait>?

查看:85
本文介绍了我可以在运行时选择特质对象而不使用Box< Trait>吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想分支并决定在运行时在函数中使用的Trait实现(请参见下面的代码示例中的poly_read).特质对象是在if表达式的分支臂内部构造的,只需要在poly_read的生命期内存在,而我需要Box,因为该特质不能从表达式臂内部借用,直至我正在尝试将其分配给的绑定.

我从逻辑上理解了为什么借用结束太早的原因,但是当绑定了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所指出的,有一种方法类似于上一个问题.

解决此问题的方法是预先声明两个必需变量:一个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围栏上的此代码..

I would like to branch and decide on a Trait implementation to use within a function at runtime (see poly_read in the code sample below). The trait object is constructed inside of the branch arms of an if expression and only needs to live for the life of poly_read yet I need to Box it because the trait can't be borrowed from within the expression arm, up to the binding that I'm attempting to assign it to.

I understand logically why the borrow ends too early, but it seems like the borrow checker should be able to extend the borrow up to the surrounding scope when the if expression's value is bound. I realize this is probably a naive notion, but I'd like to understand more about why it's not possible.

I'm a bit unhappy with the solution I have now because it requires a heap allocation even though I feel like I shouldn't need one, since I only hold onto the box for the life of the function. I suppose this is because we don't know the size of reader that would be required on the stack until the branch is taken, but couldn't it just be represented as a union in the compiler, since we at least know the maximum size.

As an aside, I actually don't know how valid my concern about the Box being heap allocated is to begin with. In general how expensive is boxing the value?

#![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);
}

解决方案

As pointed out by @Shepmaster, there is a way to do this similar to this answer from a previous question.

The way to solve this is to predeclare the two necessary variables: a File, and a 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);
}

Also see this code on the Rust playpen..

这篇关于我可以在运行时选择特质对象而不使用Box&lt; Trait&gt;吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆