I want to implement a method factory, get an error


#1
pub trait DataApi {
    fn run(&self) -> Json;
}

pub struct U01 {
    name:String,
}

impl DataApi for U01 {

    fn run(&self) -> Json {
        Json::from_str("{\"name\":\"U01\"}").unwrap()
    }

}

pub struct ApiFactory {
    map:BTreeMap<String, Box<DataApi>>,
}

impl ApiFactory {

    pub fn new() -> ApiFactory {
        let map = BTreeMap::new();
        map.insert("U01".to_string(), Box::new(U01{name:"U01".to_string()}));
        ApiFactory {
            map:map,
        }
    }

    pub fn run(&self, name:&str) -> Json {
        let api = self.map.get(name).unwrap();
        api.run()
    }

}

the error msg:

src/api/mod.rs:32:17: 32:20 error: mismatched types:
 expected `collections::btree::map::BTreeMap<collections::string::String, Box<api::DataApi + 'static>>`,
    found `collections::btree::map::BTreeMap<collections::string::String, Box<api::U01>>`
(expected trait api::DataApi,
    found struct `api::U01`) [E0308]
src/api/mod.rs:32             map:map,
                                  ^~~
src/api/mod.rs:32:17: 32:20 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to previous error

What should i do?


#2

A short example:

use std::boxed::Box;
use std::collections::HashMap;

trait Baz {
    fn get_bar(&self) -> isize;
}

struct Foo {
    pub bar: isize
}

impl Baz for Foo {
    fn get_bar(&self) -> isize {
        self.bar
    }
}

type Map = HashMap<String, Box<Baz>>;

fn main() {
    let mut map: Map = HashMap::new();
    let b: Box<Baz> = Box::new(Foo { bar: 42 }); // <<<<<<<
    map.insert("foo".to_string(), b);
}

Though some examples on doing this in-place, and on coercing struct box to trait box in-place would be nice.


#3

Thanks. It works…

but, if i share the map between threads…then an error occurs again…

the trait `core::marker::Send` is not implemented for the type `hyper_test::api::DataApi + 'static`

the trait `core::marker::Sync` is not implemented for the type `hyper_test::api::DataApi + 'static`

Then how to fix it…


#4
Box<DataApi + Send + Sync + 'static>

'static might be excess


#5

I’an new to rust.

And how make this run

use std::boxed::Box;
use std::collections::HashMap;

use std::thread;

trait Baz : Send {
    fn get_bar(&self) -> isize;
}

struct Foo {
    pub bar: isize
}

impl Baz for Foo {
    fn get_bar(&self) -> isize {
        self.bar
    }
}

type Map = HashMap<String, Box<Baz>>;

fn main() {
    let mut map: Map = HashMap::new();
    let b: Box<Baz> = Box::new(Foo { bar: 42 }); // <<<<<<<
    map.insert("foo".to_string(), b);

    thread::spawn(move || {
        let api = map.get("foo").unwrap();
        api.get_bar();
    });
    thread::sleep_ms(500);
}

#6

Read the Book: https://doc.rust-lang.org/stable/book/
It’s a good starting point.
And your thing won’t run because you haven’t synchronized access to hashmap. Briefly, Rust requires you to provide certain safety guarantees in your code.


#7

Box is in the prelude, so you don’t need use std::boxed::Box;


#8

I have read the book.
That is the problem, it only confuses me.

struct Foo {
    pub bar: isize
}

impl Baz for Foo {
    fn get_bar(&self) -> isize {
        self.bar
    }
}

type Map = HashMap<String, Box<Foo>>;

fn main() {
    let mut map: Map = HashMap::new();
    let b: Box<Foo> = Box::new(Foo { bar: 42 }); // <<<<<<<
    map.insert("foo".to_string(), b);

    thread::spawn(move || {
        let api = map.get("foo").unwrap();
        api.get_bar();
    });
    thread::sleep_ms(500);
}

This will work. So the problem is how to implement Sync and Send…
I can’t find anything about how to implement Sync and Send…My god…

There are many methods to do somthing, but the book only says you can do it…


#9
trait Baz {
    fn get_bar(&self) -> isize;
}

struct Foo {
    pub bar: isize
}

impl Baz for Foo {
    fn get_bar(&self) -> isize {
        self.bar
    }
}

type Map = HashMap<String, Baz>;

fn main() {
    let mut map: Map = HashMap::new();
    let b = Foo { bar: 42 } as Baz; // <<<<<<<
    map.insert("foo".to_string(), b);

    thread::spawn(move || {
        let api = map.get("foo").unwrap();
        api.get_bar();
    });
    thread::sleep_ms(500);
}

I want use the Foo as Baz…

How can I make the code run…


#10

It would be nice if you tell about your previous experience with other languages. Because Rust has several concepts not seen before in mainstream.

Here’s a fixed piece:

use std::sync::{Arc, Mutex};

type Map = HashMap<String, Box<Baz + Send>>;

fn main() {
    // 1. Construct map as usual
    let mut map: Map = HashMap::new();
    let b: Box<Baz + Send> = Box::new(Foo { bar: 42 });
    map.insert("foo".to_string(), b);
    // 2. Wrap it into mutexed access guard, stored in atomically refcounted smart pointer
    let arc = Arc::new(Mutex::new(map));
    // 3, Make clone of our pointer which we can send to another thread
    let inner_arc = arc.clone();
    // 4. We explicitly join over child thread and wait for its finish
    thread::spawn(move || {
        // 5. Establish lock guard, which will provide us access to stored value
        let guard = inner_arc.lock().unwrap();
        // 6. Get the value we need; Note that api will be &Baz here
        let api = guard.get("foo").unwrap();
        // 7. Call method over api
        println!("{}", api.get_bar());
    }).join();
}

Next, learn to read signatures :smile:
Your main question is, how the hell to access single object across threads.
Let’s try to check some docs:

pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static

This is std::thread::spawn.

 F: FnOnce() -> T, F: Send + 'static

says that F, which is a type of argument f``, should conform to function, and should be also sendable across threads, and, in simple case, can only consume local data by value and can reference only global constants. Also,FnOnce` denotes that this function will be run only once, and there’s no need to preserve its closure environment.

T: Send + 'static

says that that function’s return value should also be sendable across threads, and should not depend on any local data.

Note: an example of non-sendable data is Rc which is non-atomic reference-counted pointer. Since it’s non-atomic, sharing its copies across threads can lead to a big mess.

Next, you’ll need

type Map = HashMap<String, Box<Baz>>;

because you need to store some unknown object which conforms to Baz trait.

Next, to actually access value across threads, you’ll need some cross-thread storage and access protection.
The simplest cross-thread storage is std::sync::Arc, which is atomically reference counted smart pointer.
And to provide safe synchronized access, we use std::sync::Mutex. Please note that mutex here stores value, so you cannot omit locking.

To sum up, Rust is harder to learn, and much stricter. But that’s a price for safety and robustness. It’s much harder to shoot yourself in the foot than even in managed languages like C# or Java.


#11

You implementation means that api only executes at one thread.

I finnally find a way, the code as follows.

use std::boxed::Box;
use std::collections::HashMap;

use std::thread;

trait Baz: Send + Sync {
    fn get_bar(&self) -> isize;
}

struct Foo {
    pub bar: isize
}

impl Baz for Foo {
    fn get_bar(&self) -> isize {
        self.bar
    }
}

type Map = HashMap<String, Box<Baz>>;

fn main() {
    let mut map: Map = HashMap::new();
    let b = Box::new(Foo { bar: 42 }) as Box<Baz>; // <<<<<<<
    map.insert("foo".to_string(), b);

    thread::spawn(move || {
        let api = map.get("foo").unwrap();
        api.get_bar();
    });
    thread::sleep_ms(500);
}

But i still don’t know how to implement Sync and Send.


#12

Please re-read my prev. answer, there are some changes in it.
Sync and Send are special unsafe marker traits. By annotating your type with Send or Sync, you promise that your type fulfills their requirements. And you generally don’t need to do it explicitly.


#13

Yes, My question is if Foo has some private properties…How to implement Sync and Send for Foo?

Or only mutex the Foo’s private properties, or some other skill with Sync and Send?


#14

Final version, no need for explicit Send
http://is.gd/fCgX9i