Which is best design for a function taking in a trait?


#1

So, I want to write a function that takes in a mutably-borrowed trait argument, and I’m wondering what is the best code for writing the function signature. See these three options below:

use std::io;
use std::io::BufReader;

pub fn reading_func_A(reader: &mut io::BufRead) {
    reader.fill_buf(); // etc ...
}

pub fn reading_func_B<R : io::BufRead + ?Sized>(reader: &mut R) {
    reader.fill_buf(); // etc ...
}

pub fn reading_func_C<R : io::BufRead>(mut reader: R) {
    reader.fill_buf(); // etc ...
}

fn main() {
    
    let mut reader : &mut io::BufRead = &mut BufReader::new("string".as_bytes());
    loop {
        reading_func_A(reader);
        reading_func_B(reader);
        reading_func_C(&mut reader);
        
        reading_func_A(&mut BufReader::new("string".as_bytes()));
        reading_func_B(&mut BufReader::new("string".as_bytes()));
        reading_func_C(BufReader::new("string".as_bytes()));
    }
    
}

Now, from what I can tell, functions B and C are better than A, because they are parameterized, meaning they can get ‘monomorphized’ and static dispatch is used, etc. .
But what about between B and C? Are they equivalent in terms of expressive power and performance? And if so, which one would be more idiomatic (notice B and C have different calling syntax for same value)


#2

Some minor additional notes: reading_func_A and reading_func_B don’t require the reader variable/parameter to be mutable (let mut reader), but reading_func_C does, when using the reading_func_C(&mut reader) call.

However you can call reading_func_C with an reader declared as this:

let /*not mut*/ reader : &mut io::BufRead = ...

using a call like this:

fn main() {
        reading_func_C::<&mut _>(reader);
}