journey to rust
Published in

journey to rust

Implementing Display trait for a structure

I continue to do ‘more writing’ instead of reading. It does not prevent me to watch great videos on Rust (one of the motivational examples), as well as some reading, but I want to dig in.

Today I tried to implement Display trait for a trivial structure. In a process I found few details I missed before.

My final code:

use std::fmt::Display;

struct Clinic{
doctor_count: i32,
nurse_count: i32
}
impl Display for Clinic{
fn fmt (&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
let ses = |&num| match num {
1 => "",
_ => "s",
};
write!(fmt, "{} doctor{}, {} nurse{}", self.doctor_count, ses(&self.doctor_count), self.nurse_count, ses(&self.nurse_count))
}
}
fn main() {
let cl = Clinic{doctor_count: 1, nurse_count:2};
println!("{}", cl);
println!("{}", Clinic{doctor_count:2, nurse_count:1});
}

Few things I’ve learned:

The Display trait with it’s fmt function is kinky. Most languages have something here to return String. Instead, Rust requires here Result (which is reasonable, as there can be some allocations, means, failure). The more complicated thing was that Rust does not want resulting string to be in Ok. I spent some time trying to stuff into Ok a string, but only after few attempts I focused on function signature. Yep, Result with unit.

write! macro together with whole ‘formatter’ thing is a rather low-level. We have mutable state with output (formatter) and trivial output. The single reason for such protocol I may imagine is (re)allocation avoidance. Returning string sounds cool, but resulting function would need to allocate another string to put our (formatted) strings into it.

Nevertheless I don’t understand why Rust does not want to use lazy allocations here. Isn’t some kind of iterator (closure?) as return value would be better here?

Ah… The return of the closures from function. The pain point of the Rust. So, it’s easier to do macross here and do C-style ‘f — write into here’ style of arguments.

Second: I totally mess up with match. I understand it design but when time had come to write it, I forgot about ‘=>’ and used ‘:’. Nevertheless, in my first version (without closures) I was able to write let match sentence. I still need to peek for exact syntax, but at least I clearly understood what I need in place.

Closures come as second version. I done it perfect (except for few typos) — and I believe I understand what’s going on. When I create a closure, it uses ‘num’ as argument by reference (that means it does not take ownership of it), so thing passed into it can be used later. Inside closure we have no external (to closure) variables, so whole closure collapses to the simple lambda.

The last thing is a signature of the fmt function. Original value, proposed by rust compiler for some of my initial attempts was:

fn(&Clinic, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>

The part I dont’ understand is the Formatter<'_>. It’s really hard to me. Formatter is a structure with generics. But a single ‘generic’ thing here is lifetime ('), which we do not care (_).

Based on this logic I rewrote it as

fn fmt (&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error>

And it worked. That somehow give me assurance by reasoning was right. Nevertheless I can’t understand whole idea of ‘disposable on-time use lifetimes’. I played with it around, but found no solid grounds. I understand what lifetimes are, but have no intuition about them.

Nevertheless, I was really satisfying.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store