Picture of a lake in Canada

Learning Rust

May 9, 2019

In my earlier blog post about building an inky wHat dashboard I mentioned using Rust to parse the calendar feed from my Google Calendar. To get a list of events upcoming events I had previously been using a Go library which parsed the ISC file from Google Calendar however there didn’t seem to be a way to make it output repeating event occurrences. This meant that my weekly reminder to put the bins out was not being displayed on the dashboard. To fulfil this need and satisfy my curiosity I decided to build my own.

Rust

Having done some research into Rust and learning about some of its benefits I wanted to give it a go. Rust is a lower level programming language than I am used to, I have no C/C++ experience and have never needed to manage memory directly, instead I have been reliant on a garbage collector. Rust’s memory management doesn’t work in the same way as either C++ or C#, it uses the borrow checker to make its memory safety guarantees.

One of the really neat features of Rust, that it shares with Go, is its ability to cross platform compile code between different architectures. In this case I should have been able to compile for ARM on my windows development machine and then copy over a binary ready to run on the Pi. However I ran into some difficulties compiling one of the dependencies for ARM and ended up installing Rust on my Pi, copying my project’s files over and then compiling it there when I was ready.

To get stuck in and began my journey as a Rustacean I checked out the impressive documentation and getting started guides.

ISC Parser

Getting started

I had planned on using nom to parse the ISC but using it proved a bit too difficult for someone who isn’t particularly well-versed in creating parsers, macros, nor in Rust. With this in mind I decided to solve the problem in a way more familiar to me, AKA hacking on it and seeing what works and what doesn’t.

I soon learnt that the Rust compiler is not forgiving and beat me in to submission on several occasions. Having said that, it is helpful and gives actionable information on what you need to do to fix the various compile errors.

ISC Format

The abridged description of the ISC format, enough for my purposes at least is as follows:

  • 1 key value pair per line
  • The key is separated from value by a colon “:”
  • The file is separated into several sections which are nested
  • Sections are encapsulated by BEGIN and END keys, the value for both of these is the “title” for that section

An actual calendar event looks similar to the following:

BEGIN:VEVENT
DTSTART;TZID=Europe/London:20181114T212500
DTEND;TZID=Europe/London:20181114T213000
RRULE:FREQ=WEEKLY;WKST=MO
DTSTAMP:20181229T182825Z
UID:[email protected]
CREATED:20181108T072208Z
DESCRIPTION:
LAST-MODIFIED:20181108T072208Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Bins
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:This is an event reminder
SUMMARY:Alarm notification
ATTENDEE:mailto:[email protected]
TRIGGER:-P1D
END:VALARM
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:This is an event reminder
TRIGGER:-P0DT0H15M0S
END:VALARM
END:VEVENT

I parse the whole file but really only use a couple of the keys:

  • RRULE - Repetition rule
  • DTSTART - Event start date
  • DTEND - Event end date
  • SUMMARY - Event title

FYI there is no error handling in my parser so any malformed ISC will cause the app to crash and burn.

The Code / Learning Rust

isc-parser on GitLab

I made a start on the code pretty much straight away but soon realised that I need to run through the intro guide and some hello world type exercises a few times. Rust is really different to anything I have written before, and the borrow checker caused me a fair bit of grief before I got to grips (in some small way!) with how it works.

I started with my code all in one file, but that was getting unmanageable pretty quickly so I wanted to split it out into separate files, similar to how you would in any other languages. For some reason I struggled with this and I am not sure that I really understand how things work, it was pretty much trial and error to get things how I wanted them (Hopefully someone can tell me what I was doing wrong in the comments!).

Functional programming

I have dabbled a little with functional programming in Scheme and a little F# but am by no means an expert! This project contains both code in OO and functional styles. I enjoyed using map and filter_map. I also thought that the match expressions were super useful and really powerful.

Dates / Times (whydidichooseaprojectwithdates)

The defacto date/time library for Rust programmers is “chrono”, I was surprised that there wasn’t an in-built datatype. Unfortunately it isn’t quite as feature complete as Go/C#/Python and the date addition left me needing to put some hacks in place. Therefore this code is not ready for production use. I do not understand the intricacies of dates and times so I wrote only what would work for me and my very few test cases. I completely ignore time-zones in my code as they were not really necessary in my case (NB this might change once daylight savings [🤮] comes around).

Edit

Sure enough BST introduced some bugs into my little setup, so I did have to tinker a little with timezones. No doubt there are some more that I have missed but I haven’t noticed any others yet.

Benchmarking

So Rust is fast, in this case the Rust parser I wrote is faster than the Go library I had been using, significantly so. However, the test cases I ran through were far from conclusive and lacked proper scientific process no doubt.

Conclusion

Rust is really cool and I enjoyed this project, however the date time support was a little frustrating. I have barely scratched the surface of Rust’s capabilities and there is a lot of other stuff to learn, such as adding parallelism with Rayon and using traits. The Rust language documentation and crate documentation has been very useful when learning new functionality. There are lots of Rustaceans on Twitter and it has been great to see how helpful the community is there as well as on the rust subreddit.

comments powered by Disqus