rust-lang / rust-lang/rust

Opaque Type (`impl Trait`) leaks `Unpin` auto-trait from internal implementation

Open
#153,575 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-auto-traits A-impl-trait C-discussion T-types
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.