Skip to content

Integration PR for #71 #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ matrix:
include:
- rust: nightly
os: linux
env: BUILD_DOCS=1
env: BUILD_DOCS=1 BUILD_BOOK=1
- rust: nightly
os: osx
osx_image: xcode9.2
Expand All @@ -24,3 +24,4 @@ script:
- cargo test --all
- cargo fmt --all -- --check
- if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi
- if [[ -n "$BUILD_BOOK" ]]; then cargo build --all && mdbook test -L ./target/debug/deps docs && mdbook build docs; fi
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ docs = []
async-task = "1.0.0"
cfg-if = "0.1.9"
crossbeam-channel = "0.3.9"
futures-preview = "0.3.0-alpha.17"
futures-timer = "0.3.0"
lazy_static = "1.3.0"
log = { version = "0.4.8", features = ["kv_unstable"] }
Expand All @@ -38,6 +37,10 @@ pin-utils = "0.1.0-alpha.4"
slab = "0.4.2"
surf = "1.0.1"

[dependencies.futures-preview]
version = "0.3.0-alpha.17"
features = ["async-await", "nightly"]

[dev-dependencies]
femme = "1.1.0"
tempdir = "0.3.7"
Empty file added docs/src/concepts/data.csv
Empty file.
34 changes: 21 additions & 13 deletions docs/src/concepts/futures.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ Remember the talk about "deferred computation" in the intro? That's all it is. I

Let's have a look at a simple function, specifically the return value:

```rust
```rust,edition2018
# use std::{fs::File, io::{self, Read}};
#
fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File.open(path)?;
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
contents
Ok(contents)
}
```

Expand All @@ -64,12 +66,14 @@ Note that this return value talks about the past. The past has a drawback: all d

But we wanted to abstract over *computation* and let someone else choose how to run it. That's fundamentally incompatible with looking at the results of previous computation all the time. So, let's find a type that *describes* a computation without running it. Let's look at the function again:

```rust
```rust,edition2018
# use std::{fs::File, io::{self, Read}};
#
fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File.open(path)?;
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
contents
Ok(contents)
}
```

Expand All @@ -79,10 +83,11 @@ This is the moment where we could reach for [threads](https://en.wikipedia.org/w

What we are searching for is something that represents ongoing work towards a result in the future. Whenever we say "something" in Rust, we almost always mean a trait. Let's start with an incomplete definition of the `Future` trait:

```rust
```rust,edition2018
# use std::{pin::Pin, task::{Context, Poll}};
#
trait Future {
type Output;

fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
}
```
Expand All @@ -105,14 +110,17 @@ Note that calling `poll` again after case 1 happened may result in confusing beh

While the `Future` trait has existed in Rust for a while, it was inconvenient to build and describe them. For this, Rust now has a special syntax: `async`. The example from above, implemented with `async-std`, would look like this:

```rust
use async_std::fs::File;

```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::{fs::File, io::Read};
# use std::io;
#
async fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File.open(path).await?;
let mut file = File::open(path).await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
contents
Ok(contents)
}
```

Expand Down
53 changes: 39 additions & 14 deletions docs/src/concepts/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ Now that we know what Futures are, we want to run them!

In `async-std`, the [`tasks`][tasks] module is responsible for this. The simplest way is using the `block_on` function:

```rust
use async_std::fs::File;
use async_std::task;

```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::{fs::File, io::Read, task};
# use std::io;
#
async fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path).await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
contents
Ok(contents)
}

fn main() {
Expand All @@ -31,24 +33,37 @@ fn main() {

This asks the runtime baked into `async_std` to execute the code that reads a file. Let's go one by one, though, inside to outside.

```rust
```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::{fs::File, io::Read, task};
# use std::io;
#
# async fn read_file(path: &str) -> Result<String, io::Error> {
# let mut file = File::open(path).await?;
# let mut contents = String::new();
# file.read_to_string(&mut contents).await?;
# Ok(contents)
# }
#
async {
let result = read_file("data.csv").await;
match result {
Ok(s) => println!("{}", s),
Err(e) => println!("Error reading file: {:?}", e)
}
}
};
```

This is an `async` *block*. Async blocks are necessary to call `async` functions, and will instruct the compiler to include all the relevant instructions to do so. In Rust, all blocks return a value and `async` blocks happen to return a value of the kind `Future`.

But let's get to the interesting part:

```rust

task::spawn(async { })

```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::task;
task::spawn(async { });
```

`spawn` takes a `Future` and starts running it on a `Task`. It returns a `JoinHandle`. Futures in Rust are sometimes called *cold* Futures. You need something that starts running them. To run a Future, there may be some additional bookkeeping required, e.g. whether it's running or finished, where it is being placed in memory and what the current state is. This bookkeeping part is abstracted away in a `Task`.
Expand All @@ -72,7 +87,10 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread`

`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:

```rust
```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::task;
fn main() {
task::block_on(async {
// this is std::fs, which blocks
Expand All @@ -91,7 +109,10 @@ In case of `panic`, behaviour differs depending on whether there's a reasonable

In practice, that means that `block_on` propagates panics to the blocking component:

```rust
```rust,edition2018,should_panic
# #![feature(async_await)]
# extern crate async_std;
# use async_std::task;
fn main() {
task::block_on(async {
panic!("test");
Expand All @@ -106,7 +127,11 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

While panicing a spawned task will abort:

```rust
```rust,edition2018,should_panic
# #![feature(async_await)]
# extern crate async_std;
# use async_std::task;
# use std::time::Duration;
task::spawn(async {
panic!("test");
});
Expand Down
13 changes: 7 additions & 6 deletions docs/src/patterns/small-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ A collection of small, useful patterns.

`async-std` doesn't provide a `split()` method on `io` handles. Instead, splitting a stream into a read and write half can be done like this:

```rust
use async_std::io;

async fn echo(stream: io::TcpStream) {
```rust,edition2018
#![feature(async_await)]
# extern crate async_std;
use async_std::{io, net::TcpStream};
async fn echo(stream: TcpStream) {
let (reader, writer) = &mut (&stream, &stream);
io::copy(reader, writer).await?;
io::copy(reader, writer).await;
}
```
```
2 changes: 1 addition & 1 deletion docs/src/security/policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This policy is adapted from the [Rust project](https://www.rust-lang.org/policie

## PGP Key

```
```text
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBF1Wu/ABCADJaGt4HwSlqKB9BGHWYKZj/6mTMbmc29vsEOcCSQKo6myCf9zc
Expand Down
63 changes: 45 additions & 18 deletions docs/src/tutorial/accept_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@

Let's implement the scaffold of the server: a loop that binds a TCP socket to an address and starts accepting connections.


First of all, let's add required import boilerplate:

```rust
```rust,edition2018
#![feature(async_await)]

use std::net::ToSocketAddrs; // 1

# extern crate async_std;
use async_std::{
prelude::*, // 2
task, // 3
task, // 3
net::TcpListener, // 4
};
use std::net::ToSocketAddrs; // 1

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; // 5
```
Expand All @@ -29,10 +27,19 @@ type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>
To propagate the errors, we will use a boxed error trait object.
Do you know that there's `From<&'_ str> for Box<dyn Error>` implementation in stdlib, which allows you to use strings with `?` operator?


Now we can write the server's accept loop:

```rust
```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::{
# net::TcpListener,
# prelude::Stream,
# };
# use std::net::ToSocketAddrs;
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1
let listener = TcpListener::bind(addr).await?; // 2
let mut incoming = listener.incoming();
Expand All @@ -50,20 +57,40 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1
Mirroring API of `std` is an explicit design goal of `async_std`.
3. Here, we would like to iterate incoming sockets, just how one would do in `std`:

```rust
let listener: std::net::TcpListener = unimplemented!();
for stream in listener.incoming() {

}
```
```rust,edition2018,should_panic
let listener: std::net::TcpListener = unimplemented!();
for stream in listener.incoming() {
}
```

Unfortunately this doesn't quite work with `async` yet, because there's no support for `async` for-loops in the language yet.
For this reason we have to implement the loop manually, by using `while let Some(item) = iter.next().await` pattern.
Unfortunately this doesn't quite work with `async` yet, because there's no support for `async` for-loops in the language yet.
For this reason we have to implement the loop manually, by using `while let Some(item) = iter.next().await` pattern.

Finally, let's add main:

```rust
fn main() -> Result<()> {
```rust,edition2018
# #![feature(async_await)]
# extern crate async_std;
# use async_std::{
# net::TcpListener,
# prelude::Stream,
# task,
# };
# use std::net::ToSocketAddrs;
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
# async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1
# let listener = TcpListener::bind(addr).await?; // 2
# let mut incoming = listener.incoming();
# while let Some(stream) = incoming.next().await { // 3
# // TODO
# }
# Ok(())
# }
#
// main
fn run() -> Result<()> {
let fut = server("127.0.0.1:8080");
task::block_on(fut)
}
Expand Down
40 changes: 25 additions & 15 deletions docs/src/tutorial/all_together.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,46 @@

At this point, we only need to start the broker to get a fully-functioning (in the happy case!) chat:

```rust
```rust,edition2018
#![feature(async_await)]

use std::{
net::ToSocketAddrs,
sync::Arc,
collections::hash_map::{HashMap, Entry},
# extern crate async_std;
# extern crate futures;
use async_std::{
io::{self, BufReader},
net::{TcpListener, TcpStream},
prelude::*,
task,
};

use futures::{
channel::mpsc,
SinkExt,
};

use async_std::{
io::BufReader,
prelude::*,
task,
net::{TcpListener, TcpStream},
use std::{
collections::hash_map::{HashMap, Entry},
net::ToSocketAddrs,
sync::Arc,
};

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
type Sender<T> = mpsc::UnboundedSender<T>;
type Receiver<T> = mpsc::UnboundedReceiver<T>;


fn main() -> Result<()> {
// main
fn run() -> Result<()> {
task::block_on(server("127.0.0.1:8080"))
}

fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
where
F: Future<Output = Result<()>> + Send + 'static,
{
task::spawn(async move {
if let Err(e) = fut.await {
eprintln!("{}", e)
}
})
}

async fn server(addr: impl ToSocketAddrs) -> Result<()> {
let listener = TcpListener::bind(addr).await?;

Expand Down
Loading