Simon
Simon

Reputation: 890

Array of methods in Rust

I'm trying to write a requests "router" in Rust: a list of expressions associated to the functions to call when a match occurs. The functions might be methods coming from various objects, which of course leads the borrow checker to complain. Here's a sample of what I'd like:

use std::collections::HashMap;

struct Foo(bool);

impl Foo {
    fn say(&self) {
        println!("I'm {}", self.0);
    }
    fn morph(&mut self) {
        self.0 = !self.0;
    }
}

fn main() {
    let mut foo = Foo(true);

    let mut routes: HashMap<String, Box<FnMut()>> = HashMap::new();
    routes.insert("foo/say".to_string(), Box::new(|| foo.say()));  //< First (immutable) borrow of foo
    routes.insert("foo/morph".to_string(), Box::new(|| foo.morph())); //< Second (mutable) borrow of foo
    routes.insert("bar".to_string(), Box::new(|| println!("hello"))); //< Compiler also says immutable foo is used here
}

I understand why the borrow checker isn't happy about this, but I'm wondering what might be the idiomatic way of implementing that in Rust.

Side note: any general comment on the best way to get a list/array/hashmap or any collection of heterogeneous functions will be appreciated.

Upvotes: 0

Views: 261

Answers (1)

S&#233;bastien Renauld
S&#233;bastien Renauld

Reputation: 19662

The solution is quite simple, really. Because you're going to need a structure that allows you to obtain a mutable borrow on-demand, you're going to want a RwLock. And because I'm guessing what you're building is an HTTP router, to bypass lifetime requirements on Foo, you're going to want to wrap that RwLock in an Arc, like so:

use std::collections::HashMap;
use std::sync::{Arc, RwLock};

struct Foo(bool);

impl Foo {
    fn say(&self) {
        println!("I'm {}", self.0);
    }
    fn morph(&mut self) {
        self.0 = !self.0;
    }
}

fn main() {
    let mut foo = Arc::new(RwLock::new(Foo(true)));

    let mut routes: HashMap<String, Box<FnMut()>> = HashMap::new();
    routes.insert("foo/say".to_string(), Box::new(|| foo.read().unwrap().say()));  //< First (immutable) borrow of foo
    routes.insert("foo/morph".to_string(), Box::new(|| foo.write().unwrap().morph())); //< Second (mutable) borrow of foo
    routes.insert("bar".to_string(), Box::new(|| println!("hello"))); //< Compiler also says immutable foo is used here
}

Do note that I've abused the fact that everything can just borrow foo read-only for this. If you ever need to move stuff in closures, Arc implements Clone, so you should definitely take advantage of this.

The rest is fine - as long as your closures all have the same signature, boxing them and storing them in any collection is idiomatic.

Upvotes: 2

Related Questions