1 Mississippi, 2 Mississippi…

Where I learn about crates and modules and time… and sheep

Recycle logo on bag
Re-use code to make their owners proud!

Let’s learn about stealing reusing code! Rust has a developing library of code, from contributors, that you pull into your own programs and then not have to reinvent the wheel. Perl calls these modules and has CPAN as a repository and for searches, updates, and documentation. In Rust, these are instead called library crates and you can look around at the crates.io repository and others – plus create your own, internal repositories.

Searching Through Crates

For instance, go to crates.io and search for “chrono“. You should come to the chrono crates.io page. Right now, I’m seeing the most recent version as 0.4.7. Note if you had searched for “date time” instead, you’ll find many results, and the one with the huge number of downloads is chrono!

Like CPAN, crates.io can tell you the latest version (and how recent it is), if the current build of the crate is passing tests, usage information, and more. Note that if you click on the documentation link on the chrono crate page, you are taken to the docs.rs page: docs.rs/chrono/0.4.6/chrono/ and can read the full documentstion.

Now, we come to the term modules as it applies to Rust. A Rust crate has a default module, but can also have additional modules, providing additional features. For instance, from the docs.rs page about chrono, click modules on the left-side and you will see which modules are a part of chrono: format, naive, offset, prelude, and serde. We won’t need any of those additional modules, just some pieces of what the default chrono crate provides.

Rust Chrono Crate

Sheep and lamb looking at us
One second for every sheep in the world – plus some sheep left over.

I see our next Exercism problem uses the chrono crate in the solution (coincidence?!) Let’s take a look! So, they want us to display the date and time when 1 Gigasecond, from a given date and time, would occur. That is, given a date and time, figure out the date and time 1,000,000,000 seconds later. Who needs this? I dunno, but who cares about that.

Looking in the tests/gigasecond.rs file from Exercism, where the tests for this package are stored, the first one will be giving our code Utc.ymd(2011, 4, 25).and_hms(0, 0, 0) and expecting back Utc.ymd(2043, 1, 1).and_hms(1, 46, 40). Looks like a gigasecond is just shy of 32 years. If we check tests by running cargo test, we can see, of course, that it fails – we haven’t done any work yet!




Crate Semantic Versioning

Exercism did help us out though – they had already figured out the most popular crate to use for date/time types and added it as a dependency to our project. Check out the included Cargo.toml file and notice the one dependency at the end: chrono = “0.4”. Cargo looks on crates.io by default, and the version number listed is a semver (for semantic versioning). It is like a pattern to be used to determine which versions are allowed to be used for this project. In this case, specifying 0.4 means the latest 0.4.x version of the library should be pulled (so it will pull the 0.4.7 version we saw as the latest), but Cargo will not advance to 0.5. You’d have to test 0.5 and, if your code still works, modify your toml file manually. This prevents a potentially major upgrade of a library to suddenly break your code in your production deployment cycle.

Geese
We don’t tweet, we jabber – but not about Rust!

Enough jabbering – with all of the help (and documentation) chrono gives us, and all that background knowledge, I turn this into a single expression like prior problems we’ve had:

 use chrono::{DateTime, Utc, Duration}; 

 pub fn after(start: DateTime) -> DateTime {
     start + Duration::seconds(1_000_000_000)
 }

Exercism gave us use chrono::{DateTime, Utc}; in the answer already, I just tacked on “, Duration” when I realized I needed that struct of the chrono crate as well. We are passed in the parameter start, we add 1,000,000,000 seconds as a Duration type. Then the expression calculates and returns a DateTime result.

We could have written this without the use statement but I hope you’ll see its help in shortening the code is worth it:

 pub fn after(start: chrono::DateTime)
 -> chrono::DateTime {
     start + chrono::Duration::seconds(1_000_000_000)
 }

Light’s Green, Trap is Clean

Anyway, running cargo test now is green! We also run cargo test — –ignored to run all of the additional tests the Exercism team wrote, and they all pass as well!

So, another simple answer – but ONLY because someone went to all the trouble of DateTime and Duration processing and manipulation!

Leapfrogging

Ceramic frog holding a red heart
Thirty days hath September,
April, June and November.
All the rest have thirty-one,
Excepting February alone,
And that has twenty-eight days clear
And twenty-nine in each leap year.

On to the next Exercism problem in Rust. This one is also quite simple, and a situation programmers have typically learned about: leap years. Plus, you’ll see how Rust closures help clear up my (admittedly) simple code.

Leap years aren’t like daylight savings time; they’re actually necessary. I learned that Mother Goose poem as a kid – mainly the first two lines and, to this day, it’s how I remember which months have 30/31 days.

TL;DR: if the year is divisible by 4, unless divisible by 100, unless divisible by 400, it is a leap year. So, right out of the gate you could brute-force the answer:

sub is_leap_year {
  $year = shift;
  return 0 unless year && year !~ /\D/;
  return 1 if year % 400 == 0;
  return 0 if year % 100 == 0;
  return year % 4 ? 1 : 0;
}

Sorry, I slipped into perl… I love coding in perl 5. Instead, here comes some cringe-worthy Rust code:

pub fn is_leap_year(year: u64) -> bool {
   if year == 0 { return false; }
   if year % 400 == 0 { return true; }
   if year % 100 == 0 { return false; }
   if year % 4 == 0 { return true; }
   false
}



Rust Closures

Easy enough, we accept a u64 (why use a u8 when a u64 will do?) Actually, I’m not sure why this is u64, but that is how Exercism delivered the problem. And, of course, is_leap_year should return a boolean. So, lets Rust this one up, let’s change this to use a closure. This is a good example for a closure, look how clean this is (though I had to wrap things short to fit on mobile screens):

pub fn is_leap_year(year: u64) -> bool {
  let is_divisible = |n| { year % n == 0 };

  is_divisible(4)
    && (!is_divisible(100)
        || is_divisible(400)
       )
}

We define the closure is_divisible – which takes parameter n and performs a modulo against year with it and returns the remainder – and then our function is, once again, a single expression returning the true or false it evaluates to.

BTW, I am pretty sure that “and performs a modulo against year” is grammatically incorrect: programming made modulo a verb, but the dictionary still only identifies it as an adverb.

Leap year: if year divisible by 4 AND either (isn’t divisible by 100 OR is divisible by 400).

Looping in the New World

The one where I learn about Iterators

Old style life saver ring with "save me" printed on it
Save me from old-style coding techniques

I finished the “Reverse” exercise in the last post with my old-style for loop. Turns out, the mentor on Exercism had the perfect level of help in his response to my submission. He pointed out there was a more “Rust-like/idiomatic and concise manner” for looping through the characters. I did some reading.

Rust Roots and Some Suggestions

I’ve never done any functional programming before, so I’ve never used languages like Lisp, Erlang, OCaml, or Haskell (though, I’ve heard those names before). The Rust book points out that inspiration for Rust syntax and technologies was taken from several existing languages – one technology they adopted (from functional programming languages) was Iterators.

I was actually using an iterator in my first solution, with the for in loop, but I didn’t know it. The .chars() method on a String is an Iterator – it implements the std::iter.::Iterator trait. In fact, in the code for i in 1..12, the expression 1..12 is a Range<i32> value and Range<i32> is simply an iterator that spits out the integers from the starting value (inclusive) to the ending value (exclusive). Similarly, the .chars() iterator spits out the UTF-8 characters that make up the string, from the first character through the last one.

BTW, let’s try to remember this inclusive/exclusive fact – I can see it coming back to bite me if I don’t remember that the Range<i32> 1..12 would enter the loop the first time with a value of 1 and the very last time with a value 11!

Anyway, an iterator does the work of knowing how to loop through a collection of items: where to start and when to end. So my solution could have been even more antiquated; I could have had a for 0..input.len() loop.




Also, it turns out I had stumbled onto the .rev() method – which is actually a method of an Iterator type, which technically “reverses an iterator’s direction” – so it will iterate from “right to left”. It’s what I wanted, I just didn’t understand how I got there.

One final step, we want to collect the individual (reversed) string of characters that we’ll be getting out of input.char().rev(). But we don’t need to manually push them into a new string, we can simply .collect them (didn’t I say that already?) A collection of Chars will, of course, .collect into a single String. AND, since that string is exactly what we want to return, our entire function can be the single expression (with no trailing ; so Rust knows to return this value):

input.chars().rev().collect()

Turbofish – One Really Fast Fast!

One final bit of advice from the mentor on Exercism. He explained that it is best to be explicit about the resulting type with generics like .collect() by using the Rust turbofish operator ::<>. This way, it is obvious from the .collect() what type will result (though it’s plain in this simple example). So finally, we get:

pub fn reverse(input: &str) -> String {
     input.chars().rev().collect::<String>()
 }