timeit: my first tool in Rust

written by Tim Marinin, @marinintim on 2016-10-06

I was interested in Rust language for quite some time, but didn't have any practical ideas to apply it (apart from triangulation demo on opengl, which I totally should finish). I knew that I need something very simple from implementation point of view, and that it should be something practical, something that I would use later.

For some reason my zsh became really slow to load — everything was fine after it, but the initial load could be like five to fifteen seconds. And about a week ago I had an idea of a tool that would allow me to track how much time it took for zsh to load, so I decided that it was time to do it, and to do it in Rust. Below is my thoughts on this.

Introducing timeit

So there it is, right on the Github:, or grab directly from cargo install timeit-tool.

Usage is trivial:

$ timeit hello
$ # timer with title hello started
$ timeit hello
$ # call it again to stop it and get result on the stdout

Result is printed like this: timer_name nanoseconds.

Internally it uses /tmp/timeit/timer_name.tmp to store state. Apparently it works, but I'm sure that it's not particularly accurate. I just hope that it's consistently inaccurate, so I could compare different timings. Of course, as there is filesystem involved, results would be shifted due to caching and the time it takes to access and write to files, though *tmp is often backed by RAM, which speeds things up.

Idea, or how it should work

When you start timeit with timer, it would create file with current time in *tmp*timeit directory if it doesn't exist, or read that file and print the difference between current time and the one stored in file.

Rust experience

It was mostly OK. Yet, comparing to JS, Rust made me feel stupid. Don't get me wrong, timeit is a trivial app (total: 113 lines). Yet it took me almost eight hours to do the thing (not in one go, though (pun not intended (look at that lispy thing I did here))).

Rust language

I didn't experience borrow checker problems that I heard about, mostly due to tiny size of the program. But what I did experience was cryptic error messages from a compiler like this:

Compiling timeit v0.1.0 (file:///home/mt/src/timeit)
<std macros>:2:1: 6:49 error: match arms have incompatible types [E0308]
<std macros>:2 match $ expr {
src* 78:41 note: in this expansion of try! (defined in <std macros>)
<std macros>:2:1: 6:49 help: run `rustc --explain E0308` to see a detailed explanation
<std macros>:2:1: 6:49 note: expected type `_`
<std macros>:2:1: 6:49 note:    found type `()`
<std macros>:2:1: 6:49 note: expected enum `std::result::Result`, found ()
<std macros>:2 match $ expr {
src* 78:41 note: in this expansion of try! (defined in <std macros>)
<std macros>:3:46: 3:49 note: match arm with an incompatible type
<std macros>:3 $ crate :: result :: Result :: Ok ( val ) `> val , $ crate :: result :: Result
src* 78:41 note: in this expansion of try! (defined in <std macros>)
error: aborting due to previous error
error: Could not compile `timeit`.

Expected type _, found type (). Wha-a-at? And in order to fix this I did things like putting Ok(()) at the end of a function. Ok(()) resembles the magic incantations of evil sorcerers, it looks cryptic to me, and this error doesn't make sense. I mean, in the end it does makes sense, but I'm not sure that `:3 $ crate :: result :: Result :: Ok ( val ) `> val , $ crate :: result :: Result` is helping me understand what is exactly wrong in code.

Rust made me think about handling errors (which is good) and what should be passed as &Path or as Path (which doesn't make sense to me yet, due to my JS background). The task that I'm trying to accomplish is not rocket sciense: try to read file, parse it, print string and a number.

Rust documentation

Almost all libraries, including the standard library are documented in rustdoc format. It is terse and mostly without examples how to actually use this. For example (hehe), I decided to use time crate, and that they have in documentation is a list of methods and structs, with almost no top-level overview when to use particular struct. Like godoc, it is documentation for those who already knows what and how should they do.

But Rust community was welcoming. I asked for review at ##Rust@FreeNode, and @bstrie looked through my code, and thanks to his advice I was able to reduce my try! usage, which also allowed to remove this Ok(()) thing. Thanks again!


Cargo publishing process is quite polished, the only trouble I had is that timeit package name was already taken on crates, I should have check it before creating repository and things, but I was able to quickly rename crate to timeit-tool and publish it.

Final thoughts

Rust didn't make it easier to write that program for me. If I were to write it in JS, I would get it done in like ten-fifteen minutes (mostly waiting for npm install to finish). But if I were to write it in C, I'm sure that I'd make more mistakes, it would take more time and the errors would even more indecipherable.

So I want to write something else in Rust, this time something bigger and more complex.

And yes, about profiling zsh: I did get some results and would publish analysis in a few days. It's mostly nvm, sigh.

(I'd like to hear your thoughts on this post, ping me on @marinintim)

You can subscribe to my newsletter, which is about the same things as my posts, but in your inbox instead of the browser.

powered by TinyLetter