What I want to do is a way to express the following <T:trait1+trait2+trait3>mpsc::channel::<T>()
More details?
sorry for the confusion I just updated the question body
Can you post some more details, e.g. the function or struct you want to assign the generic parameters on?
sure,
I have a a bunch of store traits like session store user store etc. I also have a default store struct implementation behind a feature that implements all of them.
I am writing an app with actix-web:2.0 and postgres and both uses tokio runtime so I cant start them in the same thread so I have created a function which fires a thread to initialize my postgres store and sends it through the channel back to the actix thread so I can add it as app_data.
The function that starts my store is taking the sender part of the channel however I cannot figure out what sort of type parameter should I use while creating the channel
To answer your specific question, you can specify multiple bounds on a type using where clauses:
fn my_func<T>(chan: Sender<T>)
where
T: Trait1,
T: Trait2,
{ ... }
But regarding your multiple Tokio runtimes, I'm guessing you're talking about the panic you get by using the postgres
crate inside an existing Tokio runtime? In that case, you should know that this crate is just a wrapper around the tokio-postgres
crate, adding another Tokio runtime around that crate. So if you already have a Tokio runtime, you should just use tokio-postgres
directly.
@alice I am using r2d2 which has only postgres version unfortunately.
Take a look at this thread, and try perhaps the bb8
crate (with bb8-postgres
).
thanks
Another problem with that is that my trait is not async I tried to make an async version as well but I was frusturated with the lifetimes and everything using async-trait crate so I gave up on that so I dont really think that will suit my case
What does your trait do? The sync/async distinction isn't relevant in all code, and it is indeed cumbersome when dealing with traits. Perhaps you can post some more details?
UserStore trait contains several methods such as add_user remove_user which returns
Result<UserStore::Output,UserStore::Error>
SessionStore has veri similar structure for session management etc.
I initially tried using async-trait and creating 2 versions of the Store traits like UserStoreAsync and UserStore but It is really a challenge and since async/await ecosystem is not really solid yet I dont also want to involve my app logic with async stuff
Why does it need to be a trait? Do you have several user store types?
for development it is all postgres but once finish the development stage I will be implementing different backends for different things such as redis for session store and postgres for the user store.
This way I will be able to use all my functions the same way without actually changing their signature. At least that is what I am hoping for.
This is the second version of the app actually. the previous version was written in actix-web 0x versions and there were a lot of couplings between app logic storage and the delivery which at the moment makes it really difficult to refactor which is why I am rewriting the entire thing and trying to divide small independently updetable pieces so in the future if something happens with actix versions again I can update just the delivery part or If I am not satisfied about something with my storage solution I can update it behind the scenes without actually concerning rest of my delivery and app logic
It's true that traits are not as simple to use as concrete methods when dealing with async, but it should work ok if you use Pin<Box<dyn Future>>
as the return type (which is what the async-trait crate does behind the scenes).
The trouble is though: If you insert a synchronous layer before the IO code, then you either have to block, limiting you to four or eight concurrent users, or send it off to another thread, meaning you can't wait for the completion of the operation unless you give back some sort of future (e.g. an oneshot channel) back to the async part of the code.
Generally code in Rust becomes simpler if you limit the use of traits, because Rust is not object oriented, and you aren't really supposed to use the pattern where you make a trait for a bunch of types and just have a single (or two) implementors.
In my case I am using actix_web::web::block
function to send the blocking code off.
This was the main reason why I abandoned creating async versions of the things, at runtime tokio can handle sync code without blocking the rest of the system this way.
Am I thinking wrong about this ?
I mean that is an okay way of doing. Tokio will start up to 512 threads for handling blocking things you spawn, so you can have up to 512 of them running concurrently. It still uses a full thread per blocking task, but at least it goes in a pool with a lot of threads.
Thanks for all the Ideas I will rethink about my design.
I think instead of hiding details behind a trait I could use an opaque type. Actually I think that is what I am going to do.
That way I will be working with a solid type always and without changing anything on the api workspace I can just update the storage workspace
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.