You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[impl Trait] Should we allow impl Trait after -> in fn types or parentheses sugar? #45994
This tracking issue exists to discuss -- if we were to allow them -- what semantics they ought to have. Some known concerns around the syntax are:
Should the () switch from existential to universal quantification and back?
I think the general feeling here is now "no", basically because "too complex".
If HRTB were introduced, where would we (e.g.) want impl OtherTrait to be bound?
For consistency, we are disallow fn(impl SomeTrait) -> impl OtherTrait and dyn Fn(impl SomeTrait) -> impl OtherTrait as well. When considering the questions, one should also consider what the meaning would be in those contexts.
How is impl Trait meant to work in function pointers? Do you mean Fn instead of fn?
I meant fn -- this basically gets at the heart of the question, which is the scoping of impl Trait. In particular, one might expect that
fn foo(x: fn(impl Trait))
would desugar to
fn foo<T: Trait>(x: fn(T))
Of course -- as you righly point out -- if the T were going to be scoped to x -- sort of like for<T: Trait> fn(T) -- we couldn't actually compile that with monomorphization (though we could do it with Fn traits, potentially).
I've done a bit of hunting, and I think that this is the most appropriate place to bring this up.
Allowing impl Trait in this position would allow us to define async callbacks that borrow the input parameters. This can be done with macros at the moment, but not bare async fns due to the difficulty of expressing the higher-ranked lifetime bounds involving type parameters.
I would like to be able to write something like:
impl StructWithCallback {
// This works, but requires a macro/something to convert from `async fn` to something that has the right signature.
fn new(cb: for<'r> fn(&'r mut MyStruct) -> Pin<Box<dyn Future<Output = ()> + 'r>>) -> Self {
Self {
callback: Box::new(move |s| cb(s)),
}
}
// I believe that this would also work if we allowed `impl Future` in this position.
fn from_async_fn(cb: impl for<'r> FnMut(&'r mut MyStruct) -> (impl Future<Output = ()> + 'r)) -> Self {
Self {
callback: Box::new(move |s| Box::pin(cb(s))),
}
}
}
This would also be useful in the Gotham project, where we are currently forced to pass the context object in by move, and return a tuple containing the context (which stops us from using ?). I made a spike of this a few months ago, and hit the same issue: https://github.com/alsuren/gotham/pull/3/files#r429578771
If you can't store it in a hashmap, then you can't store it in a routing table, so that's a non-starter for the Gotham use-case.
A possible way to avoid needing impl Trait in this position would be to support writing fn foo(f: impl async Fn(SomeInput) -> SomeOutput). This would do the same lifetime elision and impl Future<Output=SomeOutput> desugaring that async fn does by default, so everything would fall out nicely in the wash.
Activity
est31 commentedon May 31, 2018
How is
impl Traitmeant to work in function pointers? Do you meanFninstead offn?nikomatsakis commentedon Jun 1, 2018
@est31
I meant
fn-- this basically gets at the heart of the question, which is the scoping ofimpl Trait. In particular, one might expect thatwould desugar to
Of course -- as you righly point out -- if the
Twere going to be scoped tox-- sort of likefor<T: Trait> fn(T)-- we couldn't actually compile that with monomorphization (though we could do it withFntraits, potentially).est31 commentedon Jun 1, 2018
@nikomatsakis I see, thanks for the clarification!
alsuren commentedon May 23, 2020
I've done a bit of hunting, and I think that this is the most appropriate place to bring this up.
Allowing
impl Traitin this position would allow us to define async callbacks that borrow the input parameters. This can be done with macros at the moment, but not bareasync fns due to the difficulty of expressing the higher-ranked lifetime bounds involving type parameters.I would like to be able to write something like:
See also:
fabianx-ai commentedon May 26, 2020
@alsuren I found a syntax / way that works:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ef3ca927495338661420b93141c5d0b6
(similar to how Box itself also works with any functions)
Would that change your stance that impl Trait is needed?
alsuren commentedon May 26, 2020
@LionsAd Unfortunately, that makes StructWithCallback generic, which means that you can't store it in a vec or hashmap. https://gist.github.com/rust-play/b89d17f00f58f071555339093d76a0a3
If you can't store it in a hashmap, then you can't store it in a routing table, so that's a non-starter for the Gotham use-case.
A possible way to avoid needing
impl Traitin this position would be to support writingfn foo(f: impl async Fn(SomeInput) -> SomeOutput). This would do the same lifetime elision andimpl Future<Output=SomeOutput>desugaring thatasync fndoes by default, so everything would fall out nicely in the wash.alsuren commentedon Jul 1, 2020
https://gist.github.com/87ab93979d770314e6698a9867d1e7e5 solves this problem using a helper trait.
It might be worth waiting to see how https://github.com/gotham-rs/gotham/pull/450/files pans out, but it looks like we don't need
impl Traitin this position for the use-case I describe above.EFanZh commentedon May 14, 2021
I have a problem that is related to this issue: https://stackoverflow.com/questions/67458566/how-to-define-a-generic-function-that-takes-a-function-that-converts-a-slice-to. I want to write a generic function that takes a function that iterates a slice in different ways:
Currently, this is not possible. Is there any way to work around this issue with stable Rust? The following attempt fails:
Note that
datashould be generated inside thefoofunction.I can also use boxed trait object for this, but it requires an extra allocation:
EFanZh commentedon May 28, 2021
I came up with one solution:
But I am not sure whether it can be simplified.