From 260f76b5a6605df971575c0a9fb541f327b1bcae Mon Sep 17 00:00:00 2001 From: Danny Browning Date: Tue, 21 Aug 2018 10:23:21 -0600 Subject: [PATCH] Lib.rs Example and Async Block Callout (#1203) Add a more complete example to lib.rs, and callout that examples and snippets in the crate use an async block. --- Cargo.toml | 2 + futures/Cargo.toml | 1 + futures/examples/functional/Cargo.toml | 19 +++++++++ futures/examples/functional/src/main.rs | 48 +++++++++++++++++++++ futures/examples/imperative/Cargo.toml | 19 +++++++++ futures/examples/imperative/src/main.rs | 48 +++++++++++++++++++++ futures/src/lib.rs | 57 +++++++++++++++++++++++++ 7 files changed, 194 insertions(+) create mode 100644 futures/examples/functional/Cargo.toml create mode 100644 futures/examples/functional/src/main.rs create mode 100644 futures/examples/imperative/Cargo.toml create mode 100644 futures/examples/imperative/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index f963204539..ea74729ab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,6 @@ members = [ "futures-task", "futures-util", "futures-test", + "futures/examples/functional", + "futures/examples/imperative" ] diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 03e083b5cd..d25da3c4ec 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -32,6 +32,7 @@ futures-util = { path = "../futures-util", version = "0.3.1", default-features = [dev-dependencies] pin-utils = "0.1.0-alpha.4" +futures-executor = { path = "../futures-executor", version = "0.3.1", features = ["thread-pool"] } futures-test = { path = "../futures-test", version = "0.3.1" } tokio = "0.1.11" assert_matches = "1.3.0" diff --git a/futures/examples/functional/Cargo.toml b/futures/examples/functional/Cargo.toml new file mode 100644 index 0000000000..74189dcb74 --- /dev/null +++ b/futures/examples/functional/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "futures-example-functional" +edition = "2018" +version = "0.3.0" +authors = ["Alex Crichton "] +license = "MIT OR Apache-2.0" +readme = "../README.md" +keywords = ["futures", "async", "future"] +repository = "https://github.com/rust-lang-nursery/futures-rs" +homepage = "https://rust-lang-nursery.github.io/futures-rs" +documentation = "https://docs.rs/futures/0.3.0" +description = """ +An implementation of futures and streams featuring zero allocations, +composability, and iterator-like interfaces. +""" +categories = ["asynchronous"] + +[dependencies] +futures = { path = "../../", version = "0.3.0", features = ["thread-pool"] } diff --git a/futures/examples/functional/src/main.rs b/futures/examples/functional/src/main.rs new file mode 100644 index 0000000000..3ce65de66a --- /dev/null +++ b/futures/examples/functional/src/main.rs @@ -0,0 +1,48 @@ +use futures::channel::mpsc; +use futures::executor; //standard executors to provide a context for futures and streams +use futures::executor::ThreadPool; +use futures::StreamExt; + +fn main() { + let pool = ThreadPool::new().expect("Failed to build pool"); + let (tx, rx) = mpsc::unbounded::(); + + // Create a future by an async block, where async is responsible for an + // implementation of Future. At this point no executor has been provided + // to this future, so it will not be running. + let fut_values = async { + // Create another async block, again where the Future implementation + // is generated by async. Since this is inside of a parent async block, + // it will be provided with the executor of the parent block when the parent + // block is executed. + // + // This executor chaining is done by Future::poll whose second argument + // is a std::task::Context. This represents our executor, and the Future + // implemented by this async block can be polled using the parent async + // block's executor. + let fut_tx_result = async move { + (0..100).for_each(|v| { + tx.unbounded_send(v).expect("Failed to send"); + }) + }; + + // Use the provided thread pool to spawn the generated future + // responsible for transmission + pool.spawn_ok(fut_tx_result); + + let fut_values = rx + .map(|v| v * 2) + .collect(); + + // Use the executor provided to this async block to wait for the + // future to complete. + fut_values.await + }; + + // Actually execute the above future, which will invoke Future::poll and + // subsequenty chain appropriate Future::poll and methods needing executors + // to drive all futures. Eventually fut_values will be driven to completion. + let values: Vec = executor::block_on(fut_values); + + println!("Values={:?}", values); +} \ No newline at end of file diff --git a/futures/examples/imperative/Cargo.toml b/futures/examples/imperative/Cargo.toml new file mode 100644 index 0000000000..20af3de0ba --- /dev/null +++ b/futures/examples/imperative/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "futures-example-imperative" +edition = "2018" +version = "0.3.0" +authors = ["Alex Crichton "] +license = "MIT OR Apache-2.0" +readme = "../README.md" +keywords = ["futures", "async", "future"] +repository = "https://github.com/rust-lang-nursery/futures-rs" +homepage = "https://rust-lang-nursery.github.io/futures-rs" +documentation = "https://docs.rs/futures/0.3.0" +description = """ +An implementation of futures and streams featuring zero allocations, +composability, and iterator-like interfaces. +""" +categories = ["asynchronous"] + +[dependencies] +futures = { path = "../../", version = "0.3.0", features = ["thread-pool"] } \ No newline at end of file diff --git a/futures/examples/imperative/src/main.rs b/futures/examples/imperative/src/main.rs new file mode 100644 index 0000000000..ff1afffe27 --- /dev/null +++ b/futures/examples/imperative/src/main.rs @@ -0,0 +1,48 @@ +use futures::channel::mpsc; +use futures::executor; //standard executors to provide a context for futures and streams +use futures::executor::ThreadPool; +use futures::StreamExt; + +fn main() { + let pool = ThreadPool::new().expect("Failed to build pool"); + let (tx, mut rx) = mpsc::unbounded::(); + + // Create a future by an async block, where async is responsible for generating + // an implementation of Future. At this point no executor has been provided + // to this future, so it will not be running. + let fut_values = async { + // Create another async block, again where Future is implemented by + // async. Since this is inside of a parent async block, it will be + // provided with the executor of the parent block when the parent + // block is executed. + // + // This executor chaining is done by Future::poll whose second argument + // is a std::task::Context. This represents our executor, and the Future + // implemented by this async block can be polled using the parent async + // block's executor. + let fut_tx_result = async move { + (0..100).for_each(|v| { + tx.unbounded_send(v).expect("Failed to send"); + }) + }; + + // Use the provided thread pool to spawn the transmission + pool.spawn_ok(fut_tx_result); + + let mut pending = vec![]; + // Use the provided executor to wait for the next value + // of the stream to be available. + while let Some(v) = rx.next().await { + pending.push(v * 2); + }; + + pending + }; + + // Actually execute the above future, which will invoke Future::poll and + // subsequenty chain appropriate Future::poll and methods needing executors + // to drive all futures. Eventually fut_values will be driven to completion. + let values: Vec = executor::block_on(fut_values); + + println!("Values={:?}", values); +} \ No newline at end of file diff --git a/futures/src/lib.rs b/futures/src/lib.rs index da02004d68..89d098cfcb 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -20,6 +20,63 @@ //! threading. Large asynchronous computations are built up using futures, //! streams and sinks, and then spawned as independent tasks that are run to //! completion, but *do not block* the thread running them. +//! +//! The following example describes how the task system context is built and used +//! within macros and keywords such as async and await!. +//! +//! ```rust +//! # use futures::channel::mpsc; +//! # use futures::executor; ///standard executors to provide a context for futures and streams +//! # use futures::executor::ThreadPool; +//! # use futures::StreamExt; +//! +//! fn main() { +//! let pool = ThreadPool::new().expect("Failed to build pool"); +//! let (tx, rx) = mpsc::unbounded::(); +//! +//! // Create a future by an async block, where async is responsible for an +//! // implementation of Future. At this point no executor has been provided +//! // to this future, so it will not be running. +//! let fut_values = async { +//! // Create another async block, again where the Future implementation +//! // is generated by async. Since this is inside of a parent async block, +//! // it will be provided with the executor of the parent block when the parent +//! // block is executed. +//! // +//! // This executor chaining is done by Future::poll whose second argument +//! // is a std::task::Context. This represents our executor, and the Future +//! // implemented by this async block can be polled using the parent async +//! // block's executor. +//! let fut_tx_result = async move { +//! (0..100).for_each(|v| { +//! tx.unbounded_send(v).expect("Failed to send"); +//! }) +//! }; +//! +//! // Use the provided thread pool to spawn the generated future +//! // responsible for transmission +//! pool.spawn_ok(fut_tx_result); +//! +//! let fut_values = rx +//! .map(|v| v * 2) +//! .collect(); +//! +//! // Use the executor provided to this async block to wait for the +//! // future to complete. +//! fut_values.await +//! }; +//! +//! // Actually execute the above future, which will invoke Future::poll and +//! // subsequenty chain appropriate Future::poll and methods needing executors +//! // to drive all futures. Eventually fut_values will be driven to completion. +//! let values: Vec = executor::block_on(fut_values); +//! +//! println!("Values={:?}", values); +//! } +//! ``` +//! +//! The majority of examples and code snippets in this crate assume that they are +//! inside an async block as written above. #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(feature = "read-initializer", feature(read_initializer))]