Open-source project
zesterer/pollster avatar
zesterer/pollster

Pollster blocks a Rust thread on a future without pulling in a runtime

A minimal async executor that lets you block on a future

726 stars32 forksRustApache-2.0

At a glance

What is it?
Pollster is a small Rust crate with no dependencies that evaluates a future in place by blocking the calling thread, which is what you need when an async function has to be called from synchronous code. It also offers an attribute macro for async main and for tests, and it is explicit about the futures it cannot drive.
Who is it for?
Pollster suits a library author or a script that has to call one async function from ordinary synchronous code and does not want a runtime in its dependency tree, and it does not suit a service that needs timers, IO readiness or multi threaded scheduling.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 105 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A hundred lines that turn an async call into a blocking one

The whole crate fits in one example.

rust
use pollster::FutureExt as _;

let my_fut = async {};

let result = my_fut.block_on();

The trait is imported under a renamed form, FutureExt as _, so the extension method block_on lands on the future type without polluting the namespace with a free function. The README is blunt about the scope: that is it, that is all it does, nothing more and nothing less, and the stated benefit is not having to pull in fifty crates to evaluate a future.

The reason this problem exists is worth spelling out, because it is a good example of how a language feature spreads. Now that async functions are stable, libraries across the Rust ecosystem expose async APIs, which is great if you are building highly concurrent web applications. Many people are not building highly concurrent web applications, and they end up holding an async function they cannot easily call from synchronous code. Pollster is the answer to that specific position: it evaluates a future in place without spinning up a heavyweight runtime like tokio or async_std.

The design goal is stated in UNIX terms, do one thing and do it well, and the README says the crate has no dependencies, compiles quickly, and is composed of roughly a hundred lines of well audited code.

Blocking means parking the thread, not spinning it

The behaviour section draws a distinction that separates this from a naive busy-wait implementation, and it is the one detail worth checking before you adopt the crate for a latency-sensitive path.

Pollster will synchronously block the thread until a future completes. It will not spin. Instead it will place the thread into a waiting state until the future has been polled to completion.

That distinction is about the CPU, not about the latency. A spinning implementation would burn a core for the whole duration of the future, which for a future that waits on a timer or on a socket is catastrophic on a machine running many of them. Parking the thread hands the core back to the operating system.

It also means the crate cannot make a future finish faster. The future still has to be driven, and a future that is waiting on something the operating system has to deliver will take as long as it takes. The value of Pollster is that it lets synchronous code drive an asynchronous API without adopting an asynchronous application model, not that it makes the underlying work faster.

The obvious failure mode follows from that: a future that can only make progress when some other task is running, or when a reactor is polling for readiness, will not complete under Pollster, because there is nothing else running and nothing watching for it.

The macro feature turns async fn main into a blocking main

The crate ships an optional attribute macro, enabled through a cargo feature, and its purpose is to remove the boilerplate of writing the block_on call yourself.

rust
#[pollster::main]
async fn main() {
    let my_fut = async {};

    my_fut.await;
}

The attribute is applied to async fn main, and the body keeps its ordinary await syntax. The macro handles the blocking call underneath. The same attribute exists for tests, in the form pollster::test, which is the piece most projects will actually reach for, since it means an async test function does not need a runtime to run under.

There is one wrinkle the README handles, which is the kind of detail that saves an afternoon. If you have re-exported the crate under a different name than pollster, you have to specify it.

rust
#[pollster::main(crate = renamed_pollster)]
async fn main() {
    let my_fut = async {};

    my_fut.await;
}

The macro lives in a separate crate, pollster-macro, which is pulled in as an optional dependency and is the only dependency the package declares. The attribute crate and the runtime crate are separated so that the base crate stays free of procedural macro machinery, which is the usual Rust pattern for keeping compile times and dependency trees small.

Not every future can be driven here, and the README says so

The compatibility section is the most important paragraph in the documentation, and it is unusually candid for a crate that exists to solve a small problem.

Pollster will not work for all futures, because some require a specific runtime or reactor. The README links to the async book chapter on determining ecosystem compatibility and makes a practical suggestion: if you are already pulling in the dependencies required to create such a future, then you probably already have a block_on in your dependency tree that was designed to poll your future, so use that instead.

That is the right advice and it changes the adoption decision. The question is not whether Pollster can drive a future, but whether the future you have needs a runtime. A future built on tokio's timer or on tokio's IO driver will not complete under Pollster, because the mechanism that would wake it does not exist. A future that is pure computation, or that is driven by an explicit waker inside the future itself, will work.

So the crate is not a smaller tokio. It is a smaller way of evaluating a future that does not need one, and the moment your code touches a runtime, you have outgrown it.

If futures is already in your tree, use its block_on

The comparison section names the alternative directly. Pollster does approximately the same thing as the block_on function from the futures crate, and if you already have futures in your dependency tree you might as well use that instead.

The difference in approach is about the dependency tree rather than the behaviour. The futures crate is a large umbrella of traits and utilities for futures, and pulling it in for a single function means compiling and auditing all of it. A runtime like tokio is heavier still. Pollster offers the same operation with no dependencies at all, which matters for a library that wants to expose an async API without imposing a runtime on its users, and for a small binary where the dependency list is part of the build time budget.

The counter-argument is the one the README itself makes: if the dependency is already there, another copy of the same function is just a second implementation to keep track of. Two block_on implementations in one binary is one more thing to reason about, and no one is going to be impressed by the hundred lines.

The right framing is that Pollster exists for the case where the async dependency is not otherwise present. That is a real and common case, and it is narrow on purpose.

A workspace with an optional macro crate and a fixed MSRV

The package metadata describes the shape of the project. The crate is called pollster, the current version is 1.0.1, the edition is 2018, and the declared minimum supported Rust version is 1.69. The licence field reads MIT OR Apache-2.0, and both licence files are present in the repository, which is the standard dual licensing arrangement for Rust crates. The repository metadata for the project lists the Apache licence, while the package itself offers the choice.

The build is a cargo workspace with two members, the root package and a macro directory, and the default members are the macro crate alone. The feature is declared as a feature that depends on the optional pollster-macro dependency, which itself points at the macro directory by path.

toml
[features]
macro = ["pollster-macro"]

[dependencies]
pollster-macro = { version = "1.0.0", path = "macro", optional = true }

Development dependencies are futures-timer for timing control in tests and tokio with only the sync feature, which is a small signal: the test suite needs a timer and a synchronization primitive, not a full async runtime. The docs.rs metadata builds with all features enabled, and the repository holds an examples directory with an mpsc example, a CHANGELOG.md, a .cargo directory and GitHub workflows.

The MSRV policy is stated explicitly: the current MSRV is 1.69.0, and the crate supports compiler versions that are at least eighteen months old. It may compile with older compilers, but that is not guaranteed. The policy is what makes a small crate pleasant to depend on, since it can stay in a dependency tree for a long time without dragging a toolchain upgrade with it.

Editorial conclusion

Pollster suits a library author or a script that has to call one async function from ordinary synchronous code and does not want a runtime in its dependency tree, and it does not suit a service that needs timers, IO readiness or multi threaded scheduling. Before adopting it, check whether the future you want to drive actually works without a reactor, because the README says plainly that some futures require a specific runtime, and if your project already depends on the futures crate then its block_on is the better choice.

Frequently asked questions

What does Pollster do in Rust?

Pollster is a minimal async executor for Rust that blocks the calling thread until a future completes, using an extension trait so you call block_on on the future. It has no dependencies and is composed of roughly a hundred lines of code.

When should I use Pollster instead of futures::executor::block_on?

The README says pollster is primarily for applications that do not care to pull all of the futures crate or a runtime like tokio into their dependency tree for the sake of evaluating simple futures. If you already depend on futures, its block_on does approximately the same thing and you might as well use that.

Does Pollster work with every async future?

No. The README states that pollster will not work for all futures because some require a specific runtime or reactor, and suggests using the block_on already in your dependency tree if you have one.

How do I use Pollster in an async main or in tests?

Enable the macro feature and apply the pollster::main attribute to your async fn main, or use pollster::test for test functions. If the crate has been re-exported under another name, the attribute needs the crate argument, for example pollster::main(crate = renamed_pollster).

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. zesterer/pollster on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zesterer-pollster.svg)](https://hysenlabs.com/projects/zesterer-pollster)

Was this useful?