Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: (dyn std::any::Any + 'static) cannot be sent between threads safely #167

Closed
sbrl opened this issue Mar 31, 2020 · 4 comments
Closed

Comments

@sbrl
Copy link

sbrl commented Mar 31, 2020

Hello,

I'm a relatively new Rust programmer, and I'm implementing a web app with actix - which spawns multiple worker threads. As part of this, I have a GlobalState struct that is shared, and is available to all request handling methods. In here I have things such as a Database connection (with r2d2_sqlite) for example.

Since I'm at the beginning of building my web app, I thought it would be great to build in multi-language support. Having built web apps previously without considering this at the get-go, I've been bitten by this before.

To this end, I thought I would try using Fluent. I think I heard about it at first through a Mozilla Hacks blog post IIRC.

To do this, from the crate documentation I understand I need to parse the fluent source files and package them up into a FluentBundle<FluentResource> at run time. To hold this, I have the following struct:

#[derive(Clone)]
pub struct Translations {
    langs: HashMap<LanguageIdentifier, FluentBundle<FluentResource>>
}

It is my understanding here that I need to derive from Clone to avoid (unhelpful) errors such as (dyn std::any::Any + 'static) cannot be sent between threads safely. This has been the case already in other places in my codebase.

Unfortunately, I've run into a bit of a problem. As soon as I added the HashMap<LanguageIdentifier, FluentBundle<FluentResource>>, I started getting the following error:

error[E0277]: `(dyn std::any::Any + 'static)` cannot be sent between threads safely
  --> wopplebloxd/src/http_server/mod.rs:27:5
   |
27 |     #[actix_rt::main]
   |     ^^^^^^^^^^^^^^^^^ `(dyn std::any::Any + 'static)` cannot be sent between threads safely
   |

(full error output: https://hastebin.com/alugesigub)

From this error, it is my understanding that FluentBundle is not currently thread safe.

As a relatively new Rust programmer (I have experience with other languages though), I'm unsure as to what I should do.

  • Is this a bug in Fluent that needs fixing?
  • Perhaps I could start a separate dedicated thread to hold the FluentBundle HashMap somehow and implement a message-passing interface or something? That sounds complicated and slow.
@sbrl sbrl changed the title (dyn std::any::Any + 'static) cannot be sent between threads safely Error: (dyn std::any::Any + 'static) cannot be sent between threads safely Mar 31, 2020
@savannidgerinel
Copy link

This has already been resolved: Introduce a concurrent version of FluentBundle #163

I was running into it, too, which is why I found this thread. After investigating a bit deeper, I found that all we need to do is to instantiate fluent::concurrent::FluentBundle instead of fluent::FluentBundle.

@zbraniecki
Copy link
Collaborator

Yes! We quite recently added fluent::concurrent::FluentBundle for multithreaded use cases! Hope that helps!

@sbrl - let me know if your issue is addressed!

@sbrl
Copy link
Author

sbrl commented Apr 3, 2020

Hey, thanks for the reply @zbraniecki, @savannidgerinel! I didn't notice that because it's not actually documented in the documentation. The link there to FluentBundle sends me here, but I see a 404:

image

Using that does fix some issues, yeah! Now I get this error:

error[E0277]: the trait bound `intl::Translations: std::clone::Clone` is not satisfied
 --> wopplebloxd/src/global_state.rs:9:5
  |
9 |     pub translations : Translations
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `intl::Translations`
  |
  = note: required by `std::clone::Clone::clone`

This is because I'm doing this:

#[derive(Clone)]
pub struct GlobalState {
    pub sitename : String,
    pub db : Database,
    pub translations : Translations
}

This is my global state object for actix:

pub async fn start(&self, port: i16, global_state : GlobalState) -> std::io::Result<()> {
	let address = format!("127.0.0.1:{}", port);
	
	handlers::print_embedded_files();
	
	info!("Starting listener on http://{}", address);
	
	HttpServer::new(move || {
		App::new()
			.data(global_state.clone())
			.wrap(Logger::default())
			.route("/static/{filepath:.*}", web::get().to(handlers::handle_static))
			.route("/", web::get().to(index))
	})
		.keep_alive(120) // TODO: Read this from a config file here
		.bind(address)? //.expect("Error: Failed to bind to address (is another processs using it?)")
		.run()
		.await
}

I'm guessing that using #[derive(Clone)] here is not really the correct solution here for a global (immutable in theory in request handlers I'm pretty sure) state object?

I'm finding Rust very different to what I'm used to, so a pointer as to the correct way to implement this would be most appreciated 🙂

I guess we can call this issue solved - though I still have some issues to work out :P

@zbraniecki
Copy link
Collaborator

haha, yeah, it takes a bit to learn your way through it! As a word of encouragement - I believe it also teaches you to be a better engineer :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants