This week I learned 12 — late post
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:
- Statically
- 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++).