I currently have an understanding problem regarding the new async fn feature in traits. I hope somebody can help.
Here the problem is that the result of
check() might be a future which is not
Send, but why might it be not
Send? When something which implements
Send shouldn't this imply that everything in it is
Send? Why should it be explicitly necessary to mention that the result of
As described in the article, when an async fn accesses non-
Send data, the future that impls the async fn is itself
!Send. I think the subtlety here is that Rust never automatically assumes that any Future is
Send; it's just a normal trait, and all of the usual trait constraint rules apply. Namely if your code requires a
Send constraint, it must be added explicitly. The crux of the issue is that it isn't easy to add such a constraint right now (also as described in the article).
Unfortunately not. There are various reasons which I don't have the depth of familiarity to describe. But intuitively, just because a trait is implemented on a type does not mean that it will automatically be required by receivers of the type. The "receiver" in this case is the
do_health_check_par async fn.
For the same reason that you cannot call trait methods without declaring the trait bound/constraint. Take for instance:
// Make sure the `Add` trait is in scope so we can use it
// NB: This is not actually used.
pub fn add_two_numbers<T>(a: T, b: T) -> T
// TODO: Uncomment the following line to fix the compile error
//where T: Add<Output = T>,
a + b
The error message in this case is pretty clear that using the
+ operator requires a trait bound on
Add, which is in scope (but unused). In this case, the "receiver" of type
T doesn't know that
Add. Also notice that there are no callers of
add_two_numbers() in this example. The compiler is telling you that the function itself requires type system knowledge (provided in the function declaration) to use the
This all might seem rather obvious, but now compare this example back to the async fn in the article. One of the ideas presented is to add a trait bound on
Send in the same way that we can add a bound on
Add here to fix our error. But in that case, there is no syntax available to add the
Send bound to the trait method on the function declaration.
Thanks for your respone!
You're right: When you implement
Send for a type that doesn't mean that everything the type contains has to implement
Send also. I've seen eg here that you can even force the parts which are
!Send to stay in the current thread with Tokio.
Then it makes sense to me that you have to explicitly implement
Send for the return value of
It isn't an explicit implementation, though, it's a constraint. It's calling out the requirement in the other direction; "this type must implement
Send", rather than saying "this type implements
Send". That's why I used the term "receiver" (even if it isn't commonly used in this case). I wanted it to be clear that you are not implementing anything by adding these constraints. You are just adding a requirement for the types that you can accept.