Monthly Archives: January 2015

My experience converting a project from rust 0.12 to 1.0

So rust 1.0 is here (well, the alpha version is here)!

Let’s convert my RustyEmitter project from version 0.12.0 to 1.0.0 alpha!

I’ll clone the project again

git clone git@github.com:kentaromiura/RustyEmitter.git

cd RustyEmitter

you can now reset to this commit if you want to try out step by step:

git reset 353da85cf3b9e11d00b41ae4d53b7983a5500e8f

Try building it:

cargo build


…/RustyEmitter/src/lib.rs:4:38: 4:39 error: obsolete syntax: `|uint| -> bool` closure type syntax

…/RustyEmitter/src/lib.rs:4 pub type EventCallback<‘callback, T> = |& mut EventData<T>|:’callback;

                                                                                               ^

note: use unboxed closures instead, no type annotation needed


Ok, so they change some syntax…

After a fast google search I think it’s related to this

http://smallcultfollowing.com/babysteps/blog/2014/11/26/purging-proc/

Seems also the lifecycle syntax changed a bit.

Anyway. Let’s try to fix this then!

git checkout -b upgrade-to-rust-1_0_0

Let’s change

pub type EventCallback<‘callback, T> = |& mut EventData<T>|:’callback;

To

pub type EventCallback<‘callback, T> = FnMut(& mut EventData<T>) + ‘callback;

rerun cargo build and


lib.rs:12:1: 14:2 error: the trait `core::marker::Sized` is not implemented for the type `for<‘r> core::ops::FnMut(&’r mut std::collections::hash::map::HashMap<collections::string::String, T>) + ‘callback


Ok, so it appear that we cannot use Vec<EventCallback<‘callback, T>> anymore as EventCallback doesn’t implement Sized now, that’s because FnMut is a trait, so let’s change that so it refer to a trait object and not just a trait, to do that we’ll change that to:

Vec<&’callback mut EventCallback<‘callback, T>>

After running cargo build again we got 7 error showing.

oh well.

Let’s look at the first one:


error: the trait `core::marker::Sized` is not implemented for the type `for<‘r> core::ops::FnMut(&’r mut std::collections::hash::map::HashMap<collections::string::String, T>) + ‘callback`

…/RustyEmitter/src/lib.rs:25   fn on(&mut self, event_name: String, callback: EventCallback<‘callback, T>) {


Ok, so we’ll have to update the on signature and implementation to reflect the previous change:

fn on(&mut self, event_name: String, callback: &’callback mut EventCallback<‘callback, T>);

Trying another cargo run, we’re down to only 3 errors w00t!

Again, let’s look at the first one:


error: type `core::option::Option<&mut collections::vec::Vec<&’callback mut for<‘r> core::ops::FnMut(&’r mut std::collections::hash::map::HashMap<collections::string::String, T>) + ‘callback>>` does not implement any method in scope named `push`

…/RustyEmitter/src/lib.rs:26       self.events.get_mut(&event_name).push(callback);


So Option do not implement push, seems reasonable, this means that rust now return an Option<T>, we’ll have to change that line to use pattern matching, basically doing:

match self.events.get_mut(&event_name) {

    Some(callbacks) => callbacks.push(callback),

    None => (),

}

It’s fine to do nothing here, as we already check if the key exists, we can refactor later to remove the current has key check and use the None branch instead, but for now just to keep things easy, let’s focus on make this thing build.

next error:


type `std::collections::hash::map::HashMap<collections::string::String, collections::vec::Vec<&’callback mut for<‘r> core::ops::FnMut(&’r mut std::collections::hash::map::HashMap<collections::string::String, T>) + ‘callback>>` does not implement any method in scope named `find_mut`

…/RustyEmitter/src/lib.rs:40     match self.events.find_mut(&event_name) {


Ok, so find_mut doesn’t exist anymore, it makes sense as it’s now the same as get_mut, let’s rewrite it to be

match self.events.get_mut(&event_name) {

\O/ woot! We got it compiling.

Ok, now just check if the tests works:

cargo test

Oh! WAT! Wait, turns out that the fail! Macro doesn’t exist anymore, now it’s has been renamed in panic! -_-.

Ok, let’s find&replace

Still a lot of errors (19), but let’s go from the first down, maybe they’re all related:


error: type `&mut std::collections::hash::map::HashMap<collections::string::String, collections::string::String>` does not implement any method in scope named `find`

…/RustyEmitter/tests/emitter.rs:15         match event.find(&test) {


Ok, some as for find_mut, now it’s get.

Rerun the tests, 17 errors, next one


error: can’t infer the “kind” of the closure, explicitly annotate it. e.g. `|&:| {}`

…/RustyEmitter/tests/emitter.rs:13     emitter.on(test.clone(), |event:& mut HashMap<String, String>|{


Ok, so rust cannot infer that the closure is of a FnMut type, in order to do that we need to help rust infer the type, let’s just add & mut in front of it for now, same on line 38, 76 and 97, next:


type `&mut std::collections::hash::map::HashMap<collections::string::String, collections::string::String>` does not implement any method in scope named `find`

…/RustyEmitter/tests/emitter.rs:40         match event.find(&test) {


Just like before, change find that to get, let’s replace all event.find( to event.get( as there are at least 2 other instances of it.

Ok, last error (I hope!)


the trait `core::fmt::String` is not implemented for the type `std::collections::hash::map::HashMap<collections::string::String, collections::string::String>`

…/RustyEmitter/tests/emitter.rs:77         panic!(“the emitter must never be called instead has been called with this parameters {}”, event);


So the HashMap can’t deserialise into a String, makes sense, let’s change the error so it only returns the size of the hash map (it should never happen anyway):

panic!(“the emitter must never be called instead has been called with {} parameters”, event.capacity());

Save, rerun cargo test and… 5 new errors! -.-;;;


borrowed value does not live long enough

/Users/kentaromiura/experiments/stepbystep/RustyEmitter/tests/emitter.rs:13     emitter.on(test.clone(), & mut |event:& mut HashMap<String, String>|{


So, this error means that the temporary variable where the callback is stored expires just after the emitter.on instruction, so in order to use it we have to store it in a variable so its lifetime will be extended (the rust compiler is so kind to suggest us the solution).

So the first things one tries is to do:

let callback = & mut |event:& mut HashMap<String, String>| {

        match event.get(&test) {

          Some(thing) => {

            if *thing != data_inside {

              panic!(“the data inside the event is not what is expected, expected {}, get {}”, data_inside, *thing);

            }

          },

          None => {

            panic!(“the data expected wasn’t found”)

          },

        }

    };

    emitter.on(test.clone(), callback);

Unfortunately this don’t work, as cargo test will show:


can’t infer the “kind” of the closure, explicitly annotate it. e.g. `|&:| {}`

…/RustyEmitter/tests/emitter.rs:12     let callback = & mut |event:& mut HashMap<String, String>| {


The solution is to change the closure to explicit say that’s a FnMut one in this way:

let callback = & mut |&mut:event:& mut HashMap<String, String>| {

So just adding &mut: after the first |(pipe)

Ok, after changing everything the tests pass again, there are a number of warning,

Mostly telling me to use “”.to_string instead of String::from_str and something about some slice syntax I have no idea what’s about, but I think it’s reasonable to call it a day 🙂