'Is it possible to "combine" similar types in rust match assignment? [duplicate]

I'm trying to implement a function in rust that iterates either on the StdIn Lines or a BuffReader Lines. Both types have lines.next() method. After that, the code is just String manipulation that works.

I originally made it worked (with repetitive and implicitly-typed branches) but the two branches had similar code. This gave me the impression I can DRY/refactor my code better.

The problem is (I think) that my two match branches, in main, do not return exactly the same type. Is it possible to make them "stick together" because I'm only using one common method? (It reminds me of Python duck typing)

Here's my refacto try:

use clap::Parser;
use exitcode;
use std::any::Any;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Lines, Write};

fn colorist(lines: &mut dyn Any, mut writer: impl std::io::Write) {
    while let Some(line_wrap) = lines.next() {
        match line_wrap {
            Ok(line) => {
                // ...
                let buffer = "Some value base on line";
                match writeln!(writer, "{}", buffer){
                    Ok(()) => (),
                    Err(e) => {eprintln!("{:?}", e);}
                };
            },
            Err(e) => {eprintln!("{:?}", e);}
        }
    }
}

fn main() {
    let args = Args::parse(); // have one '--file' arg being a filepath or is a empty str ("")
    let mut writer = io::stdout();

    let mut lines: Lines<dyn Any> = match args.file.is_empty() { // either read from stdin or read the file
        false => {
            let file = File::open(args.file).unwrap();
            let reader = BufReader::new(file);
            reader.lines() // type `std::io::Lines<BufReader<File>>`
        },
        true => {
            let stdin = io::stdin();
            stdin.lock().lines() // type `std::io::Lines<StdinLock<'_>>`
        }
    };
    colorist(&mut lines, &mut writer);
}

This is a toy project, and I'm still learning rust on my free time. I might have some bad designs there and here.



Solution 1:[1]

As @PitaJ & @Chayim-friedman suggested, Either solved my problem.

I ended up with:

use either::*;

fn colorist(lines: &mut Either<Lines<BufReader<File>>, Lines<StdinLock>>, mut writer: impl std::io::Write) {...}

fn main() {
    let args = Args::parse();
    let mut writer = io::stdout();
    let stdin = io::stdin();

    let mut lines = match args.file.is_empty(){
        false => {
            let file = File::open(args.file).unwrap();
            let reader = BufReader::new(file);
            either::Left(reader.lines())
        },
        true => {
            either::Right(stdin.lock().lines())
        }
    };
    colorist(&mut lines, &mut writer);
    match write!(writer, "{}", RESET){
        _ => (),
    };
    let _ = writer.flush();
    std::process::exit(exitcode::OK);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mifour