Disclaimer: I’m still pretty new to rust, so there’s a non-zero chance some of the information below is incorrect. If you see anything wrong please let me know.

In Rust there are two ways to use polymorphism:

  1. Statically
  2. Dynamically

Static polymorphism is achieved with generic functions. Consider a function that prints the contents of a file line by line. The standard library provides a File struct that can be used to construct a BufReader.

let file = File::open("foo").unwrap();
let mut buf_reader = BufReader::new(file);

BufReader implements BufRead that affords printing lines from a file. A function to print lines using a BufReader might look like this:

fn print_lines(b: &mut BufReader<File>) {
    let mut buffer = String::with_capacity(1024);
    while let Ok(s) = b.read_line(&mut buffer) {
        if s == 0 {
            return;
        }

        print!("{}", buffer);
        buffer.clear();
    }
}

This works fine to print lines for a BufReader, however, it doesn’t work for reading lines from stdin. While the function looks exactly the same for reading lines from stdin, we’d have to write an entirely seperate function to do so. This is where polymorphism comes in.

Since BufReader and StdinLock both implement BufRead, we can rewrite print_lines to take a generic T that implements BufRead.

fn print_lines<T: BufRead>(b: &mut T) {
    let mut buffer = String::with_capacity(1024);
    while let Ok(s) = b.read_line(&mut buffer) {
        if s == 0 {
            return;
        }

        print!("{}", buffer);
        buffer.clear();
    }
}

This accomplishes our goal of printing from a file (BufReader) or stdin(StdinLock). An important note is that this decision happens statically. That is, the compiler stamps out the correct print_lines when rustc compiles our code.

The alternative to this approach is dynamic polymorphism (similar to C++’s virtual methods).

Implementing print_lines with dynamic polymorphism looks like this:

fn print_lines(b: &mut dyn BufRead) {
    let mut buffer = String::with_capacity(1024);
    while let Ok(s) = b.read_line(&mut buffer) {
        if s == 0 {
            return;
        }

        print!("{}", buffer);
        buffer.clear();
    }
}

Not much changed although, now our program only has one print_lines for both StdinLock and BufReader. This is dispatched dynamically at runtime (again similar to a virtual method call in C++).