Code change:
When run on an with elided lifetimes, generates documentation containing the anonymous elided lifetimes in the function's signature. Running on the blocking variant of the same function produces a correct signature. You can reproduce this issue by generating docs for the following code: The asynchronous function's docs show the elided lifetimes, the blocking function's docs don't: :
I agree that this makes some function signatures unnecessarily verbose. For example: ... becomes ... However, given the nuances of async functions (specifically: the referenced values must survive every state of the FSM), one could argue that showing these lifetimes in the docs might be useful(?). It's obviously not the worst problem on earth, but could someone at least shed some light on whether this behaviour is intended or not? Is this a feature or a bug? Maybe
I think it's the expected behaviour. However we can discuss about removing the elided lifetimes. In this case it's not really useful...
It's especially visually disruptive with a parameter, making simple methods look significantly more complex than they actually are. It also feels surprising to special-case async functions like this, if it makes sense to show these lifetimes symmetry with traditional functions would be nice.
We have several instances of in the Tokio codebase because the version with explicit lifetimes is simpler than the one with all the elided ones displayed. There are many cases where it would be more useful to just not show lifetimes on the method.
So I don't forget, I got halfway through debugging this and it has something to do with 'in band lifetimes': Naively changing this to use the same mechanism as .
Funnily enough, this does work with 1.34 if you add enough s: ! So in some sense this is a regression, although you needed nightly to use at the time.

Related discussion:
text: pub async fn mut_self(mut self, mut first: usize) {} } <ins> pub trait Pattern<'a> {} </ins> pub trait Trait<const N: usize> {} // @has async_fn/fn.const_generics.html // @has - '//pre[@class="rust fn"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)' pub async fn const_generics<const N: usize>(_: impl Trait<N>) {} <ins> // test that elided lifetimes are properly elided and not displayed as `'_` // regression test for #63037 // @has async_fn/fn.elided.html // @has - '//pre[@class="rust fn"]' 'pub async fn elided(foo: &str) -> &str' pub async fn elided(foo: &str) -> &str {} // This should really be shown as written, but for implementation reasons it's difficult. // See `impl Clean for TyKind::Rptr`. // @has async_fn/fn.user_elided.html // @has - '//pre[@class="rust fn"]' 'pub async fn user_elided(foo: &str) -> &str' pub async fn user_elided(foo: &'_ str) -> &str {} // @has async_fn/fn.static_trait.html // @has - '//pre[@class="rust fn"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>' pub async fn static_trait(foo: &str) -> Box<dyn Bar> {} // @has async_fn/fn.lifetime_for_trait.html // @has - '//pre[@class="rust fn"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>" pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_> {} // @has async_fn/fn.elided_in_input_trait.html // @has - '//pre[@class="rust fn"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)" pub async fn elided_in_input_trait(t: impl Pattern<'_>) {} struct AsyncFdReadyGuard<'a, T> { x: &'a T } impl Foo { // @has async_fn/struct.Foo.html // @has - '//h4[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator<Item = &usize>' pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator<Item = &usize> {} // taken from `tokio` as an example of a method that was particularly bad before // @has - '//h4[@class="method"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>" pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()> {} // @has - '//h4[@class="method"]' "pub async fn mut_self(&mut self)" pub async fn mut_self(&mut self) {} } // test named lifetimes, just in case // @has async_fn/fn.named.html // @has - '//pre[@class="rust fn"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str" pub async fn named<'a, 'b>(foo: &'a str) -> &'b str {} // @has async_fn/fn.named_trait.html // @has - '//pre[@class="rust fn"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>" pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b> {} </ins>