diff --git a/docs/book/content/types/objects/using_contexts.md b/docs/book/content/types/objects/using_contexts.md index 6a096d5e..0fa93d02 100644 --- a/docs/book/content/types/objects/using_contexts.md +++ b/docs/book/content/types/objects/using_contexts.md @@ -59,8 +59,7 @@ struct User { // Assign Database as the context type for User #[graphql_object(context = Database)] impl User { - // Inject the context by specifying an argument - // with the context type. + // Inject the context by specifying an argument with the context type. // Note: // - the type must be a reference // - the name of the argument SHOULD be `context` @@ -86,20 +85,26 @@ impl User { You only get an immutable reference to the context, so if you want to affect change to the execution, you'll need to use [interior mutability](https://doc.rust-lang.org/book/first-edition/mutability.html#interior-vs-exterior-mutability) -using e.g. `RwLock` or `RefCell`. \ -If you are using async runtime like `tokio` for mutable references you will need to use a corresponding async version of RwLock: +using e.g. `RwLock` or `RefCell`. + + + + +## Dealing with mutable references + +Context cannot be specified by a mutable reference, because concurrent fields resolving may be performed. If you have something in your context that requires access by mutable reference, then you need to leverage the [interior mutability][1] for that. + +For example, when using async runtime with [work stealing][2] (like `tokio`), which obviously requires thread safety in addition, you will need to use a corresponding async version of `RwLock`: ```rust # extern crate juniper; # use std::collections::HashMap; # use juniper::graphql_object; -# use tokio::sync::RwLock; -# -// This struct represents our context. +use tokio::sync::RwLock; + struct Database { requested_count: HashMap, } -// Mark the Database as a valid context type for Juniper impl juniper::Context for Database {} struct User { @@ -108,21 +113,15 @@ struct User { times_requested: i32, } -// Assign Database as the context type for User and envelope it in RwLock #[graphql_object(context = RwLock)] impl User { - // Inject the context by specifying an argument - // with the context type. - // Note: - // - the type must be a reference - // - the name of the argument SHOULD be `context` - fn times_requested<'db>(&self, context: &'db RwLock) -> Vec<&'db User> { + async fn times_requested<'db>(&self, context: &'db RwLock) -> Vec<&'db User> { // Acquire a mutable reference and await if async RwLock is used, - // which is neccessary if context consists async operations - // like querying remote databases - // If context is immutable use .read() on RwLock + // which is necessary if context consists async operations like + // querying remote databases. + // If context is immutable use .read() on RwLock. let mut context = context.write().await; - // Preform a mutable operation + // Preform a mutable operation. context.requested_count.entry(self.id).and_modify(|e| { *e += 1 }).or_insert(1) } @@ -137,4 +136,10 @@ impl User { # # fn main() { } ``` -Replace `tokio::sync::RwLock` with `std::sync::RwLock` if async runtime is not intended +Replace `tokio::sync::RwLock` with `std::sync::RwLock` (or similar) if you don't intend to use async resolving. + + + + +[1]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html +[2]: https://en.wikipedia.org/wiki/Work_stealing diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml index c798aa2b..c67ebe93 100644 --- a/docs/book/tests/Cargo.toml +++ b/docs/book/tests/Cargo.toml @@ -16,7 +16,7 @@ iron = "0.5" mount = "0.4" skeptic = "0.13" serde_json = "1.0" -tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] } uuid = "0.8" [build-dependencies]