anchored/lib.rs
1//! Make things anchored and forbid them from crossing `.await` point.
2//!
3//! # Quick Example
4//! The two basic things in this crate are:
5//! - [unanchored] attribute macro for marking functions and methods you want to apply the `.await`
6//! point crossing check.
7//! - [Anchored] wrapper struct to wrap over things you want to anchored.
8//!
9//! They usually work with each other like this: **First** wrap [Anchored] on the object you want it
10//! to keep away from `.await`, like `MutexGuard`, a `&mut` reference or anything else. **Then** add
11//! [unanchored] attribute to the async function / method to enable this compile time check.
12//!
13//! That's all. Now the compiler will check it for you.
14//!
15//! For example, the following code can't compile because `bar` is trying to cross the `.await`
16//! point.
17//! ```rust, ignore
18//! # use anchored::{Anchored, unanchored};
19//! # struct Bar{}
20//! # async fn async_fn(){}
21//! #[unanchored]
22//! async fn foo(){
23//! let bar = Anchored::new(Bar {});
24//! async_fn().await;
25//! drop(bar);
26//! }
27//! ```
28//! And after limiting `bar`'s scope, everything is fine.
29//! ```rust
30//! # use anchored::{Anchored, unanchored};
31//! # struct Bar{}
32//! # async fn async_fn(){}
33//! #[unanchored]
34//! async fn foo(){
35//! {
36//! let bar = Anchored::new(Bar {});
37//! }
38//! async_fn().await;
39//! }
40//! ```
41//!
42//! # Motivation
43//! Some type is not intended to be used under async context, like the blocking `Mutex` from std, or
44//! interior mutable wrapper `RefCell` or `UnsafeCell`. Keeping them across the `.await` point may
45//! cause unexpected problems like data race, dead lock etc. When using these types in an async
46//! block, we need to check it carefully to ensure they are bounded in the sync context. In other
47//! words, not crossing the `.await` point.
48//!
49//! This crate provides a way to enforce this check at compile time. Providing more safety than
50//! manually check and can avoid some checks at runtime like `RefCell` does.
51//!
52//! # How
53//! This crate is pretty simple. It brings an auto trait `Unanchored`, and opt-out it for
54//! [Anchored]. Like the `Unpin` trait and `Pin` wrapper couple from std.
55//!
56//! We use the mechanism that when converting an async block
57//! into generator, all the variables in scope before one `.await` will be captured into generator's
58//! state. By checking the converted future's type we can tell whether an [Anchored] is captured
59//! (crossed the `.await` point).
60//!
61//! # Limitation
62//! The type inference phase only cares about the "scope" and won't take "ownership" into
63//! consideration. Which means drop the variable does works fine and have no problem in runtime, but
64//! it will fail the check:
65//! ```rust, ignore
66//! # use anchored::{Anchored, unanchored};
67//! # struct Bar{}
68//! # async fn async_fn(){}
69//! #[unanchored]
70//! async fn foo(){
71//! let bar = Anchored::new(Bar {});
72//! drop(bar);
73//! async_fn().await;
74//! }
75//! ```
76//! It should be enclosed into a smaller scope like above example explicit.
77//!
78//! # Related Work
79//! [clippy][clippy] provides two lint options `await_holding_lock` and `await_holding_refcell_ref`
80//! that can check some specific lock and reference types.
81//!
82//! [clippy]: https://github.com/rust-lang/rust-clippy
83
84#![feature(negative_impls)]
85#![feature(auto_traits)]
86
87mod anchored;
88
89pub use crate::anchored::{Anchored, Unanchored};
90pub use anchored_macros::unanchored;