-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Open
Labels
A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.A-lifetimesArea: Lifetimes / regionsArea: Lifetimes / regionsC-bugCategory: This is a bug.Category: This is a bug.F-precise_capturing`#![feature(precise_capturing)]``#![feature(precise_capturing)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.WG-asyncWorking group: Async & awaitWorking group: Async & await
Description
rustc 1.20.0-nightly (f590a44ce 2017-06-27)
binary: rustc
commit-hash: f590a44ce61888c78b9044817d8b798db5cd2ffd
commit-date: 2017-06-27
host: x86_64-pc-windows-msvc
release: 1.20.0-nightly
LLVM version: 4.0
trait Future {}
impl<F> Future for Box<F> where F: Future + ?Sized {}
struct SomeFuture<'a>(&'a Client);
impl<'a> Future for SomeFuture<'a> {}
struct Client;
impl Client {
fn post<'a, B>(&'a self, _body: &B) -> impl Future + 'a /* (1) */ {
SomeFuture(self)
}
}
fn login<'a>(client: &'a Client, username: &str) -> impl Future + 'a {
client.post(&[username])
}
fn main() {
let client = Client;
let _f = {
let username = "foo".to_string();
login(&client, &username)
};
}
Since SomeFuture
borrows 'a Client
, I'd expect impl Future + 'a
to be the correct return type, but it gives this error:
error[E0700]: hidden type for `impl Future + 'a` captures lifetime that does not appear in bounds
--> src/main.rs:16:5
|
15 | fn login<'a>(client: &'a Client, username: &str) -> impl Future + 'a {
| ---- ---------------- opaque type defined here
| |
| hidden type `impl Future + 'a` captures the anonymous lifetime defined here
16 | client.post(&[username])
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add a `use<...>` bound to explicitly capture `'_`
|
15 | fn login<'a>(client: &'a Client, username: &str) -> impl Future + 'a + use<'a, '_> {
| +++++++++++++
-
Changing
_body
to have an explicit lifetime like_body: &'b B
where'b
is independent of'a
or where'a: 'b
does not change the error. This and the original error make it seem that returning animpl trait
is somehow causing the_body
parameter to get the'a
lifetime, even though it's clearly unused, let alone used in a way that it would require the'a
lifetime. -
Changing
(1)
fromimpl Future + 'a
toSomeFuture<'a>
fixes it. -
Changing
(1)
fromimpl Future + 'a
toBox<Future + 'a>
and returningBox::new(SomeFuture(self))
fixes it.
mtak-, pcpthm, alexander-irbis, kurnevsky, Aetf and 17 more
Metadata
Metadata
Assignees
Labels
A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.A-lifetimesArea: Lifetimes / regionsArea: Lifetimes / regionsC-bugCategory: This is a bug.Category: This is a bug.F-precise_capturing`#![feature(precise_capturing)]``#![feature(precise_capturing)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.WG-asyncWorking group: Async & awaitWorking group: Async & await
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
Arnavion commentedon Oct 10, 2017
From some experimentation, it seems to be because
fn foo<T>(_: T) -> impl Bar
compiles to something likefn foo<T>(_: T) -> impl Bar + 't
, where't
is the lifetime ofT
. (I don't think this relationship can be expressed in regular Rust code, though Ralith on IRC suggested one could consider the anonymous type to containPhantomData<T>
)Thus in the original code
fn post<'a, B>(&'a self, _body: &B) -> impl Future + 'a
effectively forces the return type to be bounded byB
's lifetime in addition to'a
. This is why changing_body: &B
to_body: B
does not change anything, nor does annotating the parameter as_body: &'b B
for an unconstrained'b
In light of that, here's a smaller repro:
which complains that
s2
is dropped while still borrowed because the compiler thinks it needs to live as long as_bar
.[-]impl trait with lifetime bound seems to apply the bound to other unbounded lifetimes[/-][+]impl-trait return type is bounded by all input type parameters, even when unnecessary[/+]cramertj commentedon Jan 5, 2018
This is an intentional restriction. See RFC 1951 for the reasoning.
Arnavion commentedon Jan 5, 2018
Sure. So can there be a way that I can convince the compiler that
Client::post
's andfoo
's results don't depend on all the inputs? Maybe it can be made to not override explicit lifetimes like it does in theClient::post
example? (I recognize this won't help for thefoo
example since there is no way to ascribe a lifetime to T.)The reasoning in the RFC is that eventually there will be syntax that provides control over which lifetime are and are not part of the returned existential ("Assumption 1"). But for the OP example where there already is an explicit lifetime and bound so it could be made to work today.
cramertj commentedon Jan 5, 2018
Oh, I understand what you're saying now. Yes, if the return type contains an explicit lifetime bound, the compiler should be able to understand that the returned type outlives that lifetime bound. Currently, it cannot do that if there are type parameters involved. This should be fixed. Thanks for the report!
impl Trait
(RFC 1522, RFC 1951, RFC 2071) #34511Arnavion commentedon Aug 6, 2018
This is fixed by
existential_type
existential type PostFuture<'a>: Future + 'a; fn post<'a, B>(&'a self, _body: &B) -> PostFuture<'a> {
existential type SomeTr: Tr; fn foo<T>(_t: T) -> SomeTr {
The fact that existential types intentionally don't have the "generic lifetime inheriting" behavior that impl trait has is documented here.
Updated 2023-10-09 for current syntax:
type PostFuture<'a> = impl std::future::Future<Output = ()> + 'a; fn post<'a, B>(&'a self, _body: &B) -> PostFuture<'a> {
Note that thelet f = client.post(); async { f.await }
construction is required. Justclient.post()
orlet f = client.post(); f
fail with "opaque type's hidden type cannot be another opaque type from the same scope"type SomeTr = impl Tr; fn foo<T>(_t: T) -> SomeTr {
impl Trait
should be able to capture long-lived associated types even if the trait contains a lifetime #5438559 remaining items