Top Related Projects
A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
Async version of the Rust standard library
Zero-cost asynchronous programming in Rust
Actor framework for Rust.
Tools for concurrent programming in Rust
Quick Overview
Smol is a small and fast runtime for asynchronous Rust programming. It provides a lightweight alternative to tokio, focusing on simplicity and ease of use while still offering powerful async capabilities.
Pros
- Lightweight and minimal overhead
- Easy to use and understand
- Flexible and composable with other async ecosystems
- Fast performance for many use cases
Cons
- Less feature-rich compared to larger runtimes like tokio
- Smaller ecosystem and community support
- May not be suitable for all large-scale production environments
- Limited documentation compared to more established runtimes
Code Examples
- Basic TCP echo server:
use smol::Async;
use std::net::TcpListener;
fn main() -> std::io::Result<()> {
smol::block_on(async {
let listener = Async::<TcpListener>::bind(([127, 0, 0, 1], 7000))?;
println!("Listening on {}", listener.get_ref().local_addr()?);
loop {
let (stream, peer_addr) = listener.accept().await?;
println!("Connection from {}", peer_addr);
smol::spawn(async move {
let _ = smol::io::copy(&stream, &stream).await;
}).detach();
}
})
}
- Concurrent HTTP GET requests:
use smol::Timer;
use std::time::Duration;
async fn fetch(url: &str) -> Result<String, surf::Error> {
let mut res = surf::get(url).await?;
Ok(res.body_string().await?)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
smol::block_on(async {
let urls = vec![
"https://example.com",
"https://www.rust-lang.org",
"https://github.com",
];
let fetches = urls.iter().map(|url| fetch(url));
let results = futures::future::join_all(fetches).await;
for (url, result) in urls.iter().zip(results) {
println!("{}: {} bytes", url, result?.len());
}
Ok(())
})
}
- Simple timer example:
use smol::Timer;
use std::time::Duration;
fn main() -> std::io::Result<()> {
smol::block_on(async {
println!("Waiting for 2 seconds...");
Timer::after(Duration::from_secs(2)).await;
println!("Done!");
Ok(())
})
}
Getting Started
To use smol in your Rust project, add it to your Cargo.toml
:
[dependencies]
smol = "1.2"
Then, in your Rust code, you can import and use smol like this:
use smol;
fn main() -> std::io::Result<()> {
smol::block_on(async {
println!("Hello from smol!");
Ok(())
})
}
This sets up a basic async runtime and runs a simple async block. From here, you can start building more complex async applications using smol's features.
Competitor Comparisons
A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
Pros of Tokio
- More mature and widely adopted in the Rust ecosystem
- Extensive documentation and community support
- Offers a broader range of features and utilities
Cons of Tokio
- Larger codebase and more complex API
- Steeper learning curve for beginners
- Higher compile times due to its size and feature set
Code Comparison
Tokio example:
#[tokio::main]
async fn main() {
println!("Hello, world!");
tokio::time::sleep(Duration::from_secs(1)).await;
}
Smol example:
fn main() {
smol::block_on(async {
println!("Hello, world!");
smol::Timer::after(Duration::from_secs(1)).await;
});
}
Summary
Tokio is a more comprehensive and widely-used asynchronous runtime for Rust, offering a rich set of features and extensive community support. However, it comes with a larger codebase and a steeper learning curve. Smol, on the other hand, provides a simpler and more lightweight alternative, focusing on ease of use and minimal dependencies. While Smol may be easier to get started with, it may lack some of the advanced features and optimizations found in Tokio. The choice between the two depends on the specific requirements of your project and your familiarity with asynchronous programming in Rust.
Async version of the Rust standard library
Pros of async-std
- More comprehensive and feature-rich standard library replacement
- Better documentation and examples
- Larger ecosystem and community support
Cons of async-std
- Larger codebase and longer compilation times
- Higher memory usage due to more features
- Steeper learning curve for beginners
Code Comparison
async-std example:
use async_std::task;
use async_std::fs::File;
use async_std::prelude::*;
async fn read_file(path: &str) -> std::io::Result<String> {
let mut file = File::open(path).await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
Ok(contents)
}
smol example:
use smol::fs::File;
use smol::prelude::*;
async fn read_file(path: &str) -> std::io::Result<String> {
let mut file = File::open(path).await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
Ok(contents)
}
Both libraries provide similar functionality for asynchronous file operations, but async-std offers a more comprehensive set of features and utilities beyond basic I/O operations. smol, on the other hand, focuses on providing a minimal and lightweight alternative for async programming in Rust.
Zero-cost asynchronous programming in Rust
Pros of futures-rs
- More comprehensive and feature-rich, offering a wider range of utilities and abstractions for async programming
- Officially maintained by the Rust team, ensuring long-term support and compatibility
- Extensively documented with detailed examples and explanations
Cons of futures-rs
- Larger and more complex API, which can be overwhelming for beginners
- Higher learning curve due to its extensive feature set
- May introduce more dependencies and increase compilation times
Code Comparison
futures-rs:
use futures::executor::block_on;
use futures::future::join_all;
async fn task(i: u32) -> u32 { i * 2 }
let tasks = (0..5).map(|i| task(i));
let results = block_on(join_all(tasks));
smol:
use smol::Timer;
use std::time::Duration;
smol::block_on(async {
Timer::after(Duration::from_secs(1)).await;
println!("One second has passed");
});
Summary
futures-rs is a more comprehensive and officially supported library, offering a wide range of async programming tools. It's ideal for complex projects requiring advanced features. smol, on the other hand, provides a simpler and more lightweight approach to async programming, making it easier to get started with and potentially faster for simpler use cases. The choice between the two depends on the specific needs of your project and your familiarity with async Rust programming.
Actor framework for Rust.
Pros of Actix
- Higher performance and scalability for web applications
- More comprehensive ecosystem with additional tools and integrations
- Better suited for large-scale, production-ready applications
Cons of Actix
- Steeper learning curve and more complex API
- Heavier runtime overhead compared to Smol
- Less flexibility for non-web async workloads
Code Comparison
Actix example:
use actix_web::{web, App, HttpServer, Responder};
async fn hello() -> impl Responder {
"Hello, World!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().route("/", web::get().to(hello))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Smol example:
use async_std::net::TcpListener;
use async_std::prelude::*;
use smol::Task;
fn main() -> std::io::Result<()> {
smol::block_on(async {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
let mut incoming = listener.incoming();
while let Some(stream) = incoming.next().await {
let stream = stream?;
Task::spawn(async move {
// Handle connection
}).detach();
}
Ok(())
})
}
Tools for concurrent programming in Rust
Pros of Crossbeam
- More mature and widely adopted in the Rust ecosystem
- Provides a broader range of concurrent programming primitives
- Highly optimized for performance in multi-threaded scenarios
Cons of Crossbeam
- Steeper learning curve due to its extensive feature set
- May be overkill for simpler concurrent programming tasks
- Requires more careful consideration of memory safety
Code Comparison
Crossbeam (using channels):
use crossbeam_channel::unbounded;
let (s, r) = unbounded();
s.send(1).unwrap();
assert_eq!(r.recv(), Ok(1));
Smol (using async channels):
use smol::channel;
let (s, r) = channel::unbounded();
s.send(1).await.unwrap();
assert_eq!(r.recv().await, Ok(1));
Summary
Crossbeam is a comprehensive concurrency library with a focus on low-level primitives and high performance. It's well-suited for complex multi-threaded applications. Smol, on the other hand, is an async runtime that emphasizes simplicity and ease of use, making it a good choice for async-focused projects or those new to concurrent programming in Rust.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
smol
A small and fast async runtime.
This crate simply re-exports other smaller async crates (see the source).
To use tokio-based libraries with smol, apply the async-compat
adapter to futures and I/O
types.
See the smol-macros
crate if you want a no proc-macro, fast compiling, easy-to-use
async main and/or multi-threaded Executor setup out of the box.
Examples
Connect to an HTTP website, make a GET request, and pipe the response to the standard output:
use smol::{io, net, prelude::*, Unblock};
fn main() -> io::Result<()> {
smol::block_on(async {
let mut stream = net::TcpStream::connect("example.com:80").await?;
let req = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
stream.write_all(req).await?;
let mut stdout = Unblock::new(std::io::stdout());
io::copy(stream, &mut stdout).await?;
Ok(())
})
}
There's a lot more in the examples directory.
Subcrates
- async-channel - Multi-producer multi-consumer channels
- async-executor - Composable async executors
- async-fs - Async filesystem primitives
- async-io - Async adapter for I/O types, also timers
- async-lock - Async locks (barrier, mutex, reader-writer lock, semaphore)
- async-net - Async networking primitives (TCP/UDP/Unix)
- async-process - Async interface for working with processes
- async-task - Task abstraction for building executors
- blocking - A thread pool for blocking I/O
- futures-lite - A lighter fork of futures
- polling - Portable interface to epoll, kqueue, event ports, and wepoll
TLS certificate
Some code examples are using TLS for authentication. The repository contains a self-signed certificate usable for testing, but it should not be used for real-world scenarios. Browsers and tools like curl will show this certificate as insecure.
In browsers, accept the security prompt or use curl -k
on the
command line to bypass security warnings.
The certificate file was generated using minica and openssl:
minica --domains localhost -ip-addresses 127.0.0.1 -ca-cert certificate.pem
openssl pkcs12 -export -out identity.pfx -inkey localhost/key.pem -in localhost/cert.pem
Another useful tool for making certificates is mkcert.
MSRV Policy
The Minimum Supported Rust Version (MSRV) of this crate is 1.63. As a tentative policy, the MSRV will not advance past the current Rust version provided by Debian Stable. At the time of writing, this version of Rust is 1.63. However, the MSRV may be advanced further in the event of a major ecosystem shift or a security vulnerability.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Top Related Projects
A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
Async version of the Rust standard library
Zero-cost asynchronous programming in Rust
Actor framework for Rust.
Tools for concurrent programming in Rust
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot