Opaque Type (`impl Trait`) leaks `Unpin` auto-trait from internal implementation
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
While trying to compile the following code:
use futures::StreamExt;
fn build_stream<Func, Fut>(vec: Vec<u32>, func: Func) -> impl futures::Stream<Item = u32>
where
Func: FnMut(u32) -> Fut + Send + 'static,
Fut: Future<Output = u32> + Send + 'static,
{
futures::stream::iter(vec).then(func)
}
#[tokio::main]
async fn main() {
let a = vec![1, 2, 3, 4];
let mut stream = build_stream(a, |x| async move { x * x });
while let Some(item) = stream.next().await {
println!("{item}");
}
}
You would fairly receive the following error message
error[E0277]: `{async block@src/main.rs:15:42: 15:47}` cannot be unpinned
--> src/main.rs:17:42
|
17 | while let Some(item) = stream.next().await {
| ^^^^^ within `__Origin<'_, Iter<IntoIter<{integer}>>, ..., ...>`, the trait `Unpin` is not implemented for `{async block@src/main.rs:15:42: 15:47}`
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required because it appears within the type `Option<{async block@src/main.rs:15:42: 15:47}>`
Which is totally fine and not the issue which I want to raise.
An problem or misleading behaviour, which I am considering in this case.
Is that to fix the compilation error I could just to the following line
futures::stream::iter(vec).then(func)
to
futures::stream::iter(vec).then(func).boxed()
Not, changing the interface and Opaque type for the build_stream function, change the internal implementation of build_stream.
I am not sure is it an intentional behaviour or not, and does it reproducible in some other scenarios.
But it looks wrong to me, because the return type has not been changed at all, which kind of violates the defined contract of how Opaque Types should work and kind of leaking the internal implementation of the build_stream function with the Opaque return type to the caller. Without properly reflecting it in the Opaque type itself.
Meta
Project setup
[package]
name = "helloworld"
version = "0.1.0"
license = "MIT"
edition = "2024"
[dependencies]
futures = "0.3.32"
tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread"] }
rustc --version --verbose:
rustc 1.94.0 (4a4ef493e 2026-03-02)
binary: rustc
commit-hash: 4a4ef493e3a1488c6e321570238084b38948f6db
commit-date: 2026-03-02
host: aarch64-apple-darwin
release: 1.94.0
LLVM version: 21.1.8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the minimal example with the stated rustc and futures versions, then inspect the compiler's opaque-type and auto-trait handling. No source file or test is named in the issue; done would require a confirmed explanation and an agreed change or regression test for whether internal Unpin changes should affect callers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 28/100