How to deal with external type which is !Send and !Sync

I want to store the result of the parse done with pest in a Discord bot using the async version of serenity.

I need to impl this trait https://docs.rs/serenity/0.9.0-rc.0/serenity/prelude/trait.TypeMapKey.html
in order to pass some data to the bot commands when invoked:

struct RerollTable<'a> {
    _p: PhantomData<caith::Roller<'a>>,
}

impl<'a> TypeMapKey for RerollTable<'a> {
    type Value = HashMap<String, caith::Roller<'a>>;
}

caith::Roller being:

pub struct Roller<'a> {
    parsed: Pairs<'a, Rule>,
}

Pairs and Rule coming from pest.

Knowing Pairs is !Send and !Sync, the HashMap definition can't compile. Is there any trick/pattern to make that happened or am I in a deadend?

(PS: thanks for you answer on reddit Alice :wink: that is kind of a follow up but not related to tokio anymore, hence the new topic)

Can you wrap the offending type in an Arc<Mutex<...>>? Or maybe a RwLock instead of a Mutex if you have mostly readers.

This is a dead end. Store the data using some other type.

@dthul That only works if it is Send + !Sync. There's nothing you can do if it is not Send.

1 Like

Oh yes, you are absolutely right!

Yeah, I think I'm giving up on storing the parse result :cry:

A solution could be to spin up a dedicated thread that holds on to the parse result which is !Send and it listens on a channel whose sender end you put into the typemap instead. Might be overkill though, depending on how expensive the parsing is.

Well, it's a toy project https://github.com/Geobert/disle/ so the input should be small

but the crate https://github.com/Geobert/caith could be used in another context where parsing is expensive, that why I tried. But let's be honest, it's very unlikely x) So I won't bother and keep my String and have it parsed again on every action

And I just realize it was also you on my question on pest's Github x)

1 Like

Can you convert the caith::Roller<'a> to your own dumb AST types? That's what I usually do when parsing code.

I need to keep the pest::Pairs in order to leverage pest::PrecClimber ^^

Ah, okay. Normally I'll try to structure my grammar so you don't need to worry about precedence.

Taking another approach... Why are you implementing half the parsing process in one place and doing the rest (handling precedence) in another place? Can you leverage PrecClimber in the same place as the parser code so you just need to make sure the results are Send and Sync, and it doesn't matter what the intermediate parse values are.

Because it's a dice roller, so some node of the AST need to be recompute at each roll :smiley:

FWIW, there is a very stupid thing to do to "force-Send" non-Send data: spin a thread that holds the data, and interact with it through "messages".

Here is a Playground implementing it (where the closures need to be 'static since, for the sake of simplicity, I have used std's threading API, not crossbeam's, + I have wanted to keep the code unsafe free):

Not very useful nor advisable, and the !Send data needs to be 'static due to non-unsafe type erasure (Any), so definitely not usable for your use case.

But it could be for somebody else interested in the this thread's topic :upside_down_face:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.