Могу ли я выбрать трейт-объект во время выполнения без использования 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);
}

person JakeK    schedule 01.02.2015    source источник
comment
возможный дубликат Разрешены ли полиморфные переменные?   -  person Shepmaster    schedule 01.02.2015
comment
В частности, этот ответ на этот вопрос.   -  person Shepmaster    schedule 01.02.2015
comment
Да, спасибо @Shepmaster   -  person JakeK    schedule 01.02.2015
comment
Нет проблем, и добро пожаловать в Stack Overflow! Не забывайте голосовать за вопросы и ответы, которые вы считаете полезными. Это помогает другим людям (и вам в будущем) находить качественные ответы.   -  person Shepmaster    schedule 01.02.2015


Ответы (1)


Как указал @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.

person Wendell    schedule 16.03.2015