🚧 Attention, peinture fraîche !
Cette page a été traduite par une seule personne et n'a pas été relue et vérifiée par quelqu'un d'autre ! Les informations peuvent par exemple être erronées, être formulées maladroitement, ou contenir d'autres types de fautes.
L'approximation de Send
Certaines machines à état de fonctions asynchrones sont sûres pour être
envoyées entre des processus, alors que d'autres ne le sont pas. Le fait que la
Future
d'une fonction asynchrone est Send
ou non est conditionné par le
fait qu'un type Send
soit maintenu par un .await
, mais cette approche est
aujourd'hui trop conservatrice sur certains points.
Par exemple, imaginez un simple type qui n'est pas Send
, comme un type qui
contient un Rc
:
#![allow(unused)] fn main() { use std::rc::Rc; #[derive(Default)] struct EstPasSend(Rc<()>); }
Les variables du type EstPasSend
peuvent intervenir brièvement dans des
fonctions asynchrones même si le type résultant de la Future
retournée par la
fonction asynchrone doit être Send
:
use std::rc::Rc; #[derive(Default)] struct EstPasSend(Rc<()>); async fn beta() {} async fn alpha() { EstPasSend::default(); beta().await; } fn necessite_send(_: impl Send) {} fn main() { necessite_send(alpha()); }
Cependant, si nous changeons alpha
pour stocker le EstPasSend
dans une
variable, cet exemple ne se compile plus :
use std::rc::Rc; #[derive(Default)] struct EstPasSend(Rc<()>); async fn beta() {} async fn alpha() { let x = EstPasSend::default(); beta().await; } fn necessite_send(_: impl Send) {} fn main() { necessite_send(alpha()); }
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
-- > src/main.rs:15:5
|
15 | necessite_send(foo());
| ^^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
= note: required because it appears within the type `EstPasSend`
= note: required because it appears within the type `{EstPasSend, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@src/main.rs:7:16: 10:2 {EstPasSend, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:7:16: 10:2 {EstPasSend, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: required by `necessite_send`
-- > src/main.rs:12:1
|
12 | fn necessite_send(_: impl Send) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
Cette erreur est justifiée. Si nous stockons x
dans une variable, il ne sera
pas libéré avant d'arriver après le .await
, moment où la fonction asynchrone
s'exécute peut-être sur un processus différent. Comme Rc
n'est pas Send
,
lui permettre de voyager entre les processus ne serait pas sain. Une solution
simple à cela serait de libérer le Rc
avec drop
avant le .await
, mais
malheureusement cela ne fonctionne pas aujourd'hui.
Pour contourner ce problème, vous pouvez créer une portée de bloc qui englobe
chacune des variables qui ne sont pas Send
. Cela permet de dire facilement au
compilateur que ces variables ne vivent plus en dehors de l'utilisation du
.await
.
use std::rc::Rc; #[derive(Default)] struct EstPasSend(Rc<()>); async fn beta() {} async fn alpha() { { let x = EstPasSend::default(); } beta().await; } fn necessite_send(_: impl Send) {} fn main() { necessite_send(alpha()); }