admin管理员组

文章数量:1336311

I'm very new to rust, so I concede off the bat that there may be a better third way to do this, but I'm very interested in what someone more experienced with the language has to say.

Given a generic function with signature:

fn process_lines<T: BufRead + Sized>(reader: T, re: Regex) {

What is the more "conventional" rust approach to calling this?

There is the straight forward approach:

match input {
    "-" => {
        let stdin = io::stdin();
        process_lines(stdin.lock(), re)
    }
    _ => {
        let f = File::open(input).unwrap();
        process_lines(BufReader::new(f), re)
    }
};

In other languages I would avoid writing out function calls multiple times by doing something that I think would translate as this:

let reader: Box<dyn BufRead> = match input {
    "-" => {
        let stdin = io::stdin();
        Box::new(stdin.lock())
    }
    _ => {
        let f = File::open(input).unwrap();
        Box::new(BufReader::new(f))
    }
};

process_lines(reader, re);

Is there a better way to construct reader other than calling Box ?

This is a super trivial situation, but I'm worried I am falling into some kind of mental trap doing things the way I'm accustomed to...

I understand there is a some performance penalty for using Box... but I would imagine this is more of a problem inside some sort of nested control flow.

Which of these patterns scales better in large rust code bases, or it is always a per case kind of call?

Any and all insights on this dichotomy (or false dichotomy if there is indeed a third way) are really appreciated!

I'm very new to rust, so I concede off the bat that there may be a better third way to do this, but I'm very interested in what someone more experienced with the language has to say.

Given a generic function with signature:

fn process_lines<T: BufRead + Sized>(reader: T, re: Regex) {

What is the more "conventional" rust approach to calling this?

There is the straight forward approach:

match input {
    "-" => {
        let stdin = io::stdin();
        process_lines(stdin.lock(), re)
    }
    _ => {
        let f = File::open(input).unwrap();
        process_lines(BufReader::new(f), re)
    }
};

In other languages I would avoid writing out function calls multiple times by doing something that I think would translate as this:

let reader: Box<dyn BufRead> = match input {
    "-" => {
        let stdin = io::stdin();
        Box::new(stdin.lock())
    }
    _ => {
        let f = File::open(input).unwrap();
        Box::new(BufReader::new(f))
    }
};

process_lines(reader, re);

Is there a better way to construct reader other than calling Box ?

This is a super trivial situation, but I'm worried I am falling into some kind of mental trap doing things the way I'm accustomed to...

I understand there is a some performance penalty for using Box... but I would imagine this is more of a problem inside some sort of nested control flow.

Which of these patterns scales better in large rust code bases, or it is always a per case kind of call?

Any and all insights on this dichotomy (or false dichotomy if there is indeed a third way) are really appreciated!

Share edited Nov 19, 2024 at 18:55 dumbass 27.3k4 gold badges37 silver badges74 bronze badges asked Nov 19, 2024 at 18:35 Niall ByrneNiall Byrne 2,4601 gold badge18 silver badges19 bronze badges 3
  • Maybe this is really hinting to me that creating the reader itself should be abstracted out so that I just call: process_lines(create_reader(input), re) ? But I'd probably end up using Box again ... – Niall Byrne Commented Nov 19, 2024 at 18:39
  • This is, essentially, a missing feature, sometimes called enum impl Trait in discussions – see for example <internals.rust-lang./t/…>. – dumbass Commented Nov 19, 2024 at 18:43
  • 2 You can at least avoid the Box overhead by using &mut dyn io::BufRead instead. Declare let (mut r0, mut r1); in a scope containing the process_lines call, then have each match arm assign to either r0 or r1 and return a &mut to that variable. Then pass that reference as the argument to process_lines. – dumbass Commented Nov 19, 2024 at 18:53
Add a comment  | 

1 Answer 1

Reset to default 2

Temporary lifetime extension has been landed in rust 1.79, which makes it possible to write this:

let reader: &mut dyn BufRead = match input {
    "-" => &mut std::io::stdin().lock(),
    _ => {
        let file = std::fs::File::open(input).unwrap();
        &mut std::io::BufReader::new(file)
    }
};

process_lines(reader, re);

Temporary values that are generated in if, match, and raw blocks, with borrows that escape the scope of the block, would have extended lifetime to match the borrows' lifetime.

This is useful for many dynamic dispatch cases, which are not supported in the earlier versions.

本文标签: