question and my goal
My goal is to let this (playground) compile. Below is the core of the code. This is attempt to implement FlatMap for all F<F<A>>, such as Option<Option<i32>>.
trait HKT1 {
type Unwrapped;
type Wrapped<T>;
}
trait FlatMap: HKT1 + Sized {
fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
// Below cannot compile
fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>
// How to set generic bound correctly?
where
Self::Wrapped<Self::Wrapped<A>>: FlatMap
{
ffa.flat_map(|x| x)
}
}
error[E0308]: mismatched types
--> src/main.rs:15:26
|
15 | ffa.flat_map(|x| x)
| ^ expected HKT1::Wrapped, found HKT1::Unwrapped
|
= note: expected associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Wrapped<_>`
found associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Unwrapped`
error[E0308]: mismatched types
--> src/main.rs:15:9
|
6 | trait FlatMap: HKT1 + Sized {
| --------------------------- this type parameter
...
11 | fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>
| ---------------- expected `<Self as HKT1>::Wrapped<A>` because of return type
...
15 | ffa.flat_map(|x| x)
| ^^^^^^^^^^^^^^^^^^^ expected type parameter `Self`, found associated type
|
= note: expected associated type `<Self as HKT1>::Wrapped<A>`
found associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Wrapped<_>`
= note: you might be missing a type parameter or trait bound
HKT1 is a way to emulate F<_> and allow all traits that are based on it to access F and _ separately. I learned from this blog.
what I am trying to do
I'm trying to build up a functional programming crate which is kind of translating Scala lib cats to Rust. (Current work here). To implement the higher kinded type, I follow this blog and define the HKT1 trait. It is quite elegant until I want to implement flatten(ffa: F<F<A>>) -> F<A> but cannot figure out how to set the generic bound correctly.
It seems easy to implement if I leave it blank in the trait. For example:
trait FlatMap: HKT1 + Sized {
fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>;
}
struct MyF<T>(pub T);
impl<A> HKT1 for MyF<A> {
type Unwrapped = A;
type Wrapped<T> = MyF<T>;
}
impl<T> FlatMap for MyF<T> {
fn flat_map<B, F>(self, f: F) -> MyF<B>
where
F: FnOnce(Self::Unwrapped) -> MyF<B>,
{
f(self.0)
}
fn flatten<A>(ffa: MyF<MyF<A>>) -> MyF<A> {
ffa.flat_map(|x| x)
}
}
But I would like to have a default implementation through flat_map. How can I achieve this?
A solution via EqT
I just learned how to achieve this from the implementation of crate functional. (Well, the implementation is a little different from above)
The key idea is to add a trait EqT, whose purpose is to assert two types are equal. (playground)
trait HKT1 {
type Unwrapped;
type Wrapped<T>;
}
trait FlatMap: HKT1 + Sized {
fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
fn flatten<A>(self) -> Self::Wrapped<A>
where
Self::Unwrapped: EqT<Self::Wrapped<A>>,
{
self.flat_map(|x| x.cast())
}
}
trait EqT<T> {
fn cast(self) -> T;
}
impl<T> EqT<T> for T {
fn cast(self) -> T {
self
}
}
Then we can easily implement and use flatten:
struct MyF<T>(pub T);
impl<A> HKT1 for MyF<A> {
type Unwrapped = A;
type Wrapped<T> = MyF<T>;
}
impl<T> FlatMap for MyF<T> {
fn flat_map<B, F>(self, f: F) -> MyF<B>
where
F: FnOnce(Self::Unwrapped) -> MyF<B>,
{
f(self.0)
}
}
let ffa = MyF(MyF(1));
let fa = ffa.flatten();
println!("{:?}", fa); // Expect MyF(1)
Related
I want: for a trait, e.g. Foo, any fn(T1,T2)->() should implement it, and I don't care whether T1 or T2 is a reference or not.
trait Foo {
fn hello_world(&self) -> String {
"hello world".into()
}
}
impl<T1, T2> Foo for fn(T1, T2) -> () {}
fn a_dummy_sample_function(_: i32, _:i32) {}
fn main() {
let fn_ptr : fn(i32, i32)->() = a_dummy_sample_function;
(fn_ptr).hello_world();
}
So far, it works well.
If I change the type of first argument to &i32,
trait Foo {
fn hello_world(&self) -> String {
"hello world".into()
}
}
impl<T1, T2> Foo for fn(T1, T2) -> () {}
fn foo_imp(_: &i32, _:i32) {}
fn main() {
let fn_ptr : fn(&i32, i32)->() = foo_imp;
fn_ptr.hello_world();
}
Then, I've got the following compilation errors.
--> src/main.rs:12:12
|
12 | fn_ptr.hello_world();
| ^^^^^^^^^^^ method not found in `for<'r> fn(&'r i32, i32)`
|
= note: `fn_ptr` is a function, perhaps you wish to call it
= help: items from traits can only be used if the trait is implemented and in scope
note: `Foo` defines an item `hello_world`, perhaps you need to implement it
I can fix the compilation error as below
impl<T1, T2> Foo for for<'a> fn(&'a T1, T2) -> () {}
But it is not what I want, I want any function pointer with any types of two arguments whether it is a reference or not.
tokio::net::TcpStream::connect is an async function, meaning it returns an existential type, impl Future. I would like to store a Vec of these futures in a struct. I've found many questions where someone wants to store multiple different impl Futures in a list, but I only want to store the return type of one. I feel like this should be possible without Box<dyn Future> as I am really only storing a single concrete type, but I cannot figure out how without getting found opaque type errors.
It is possible with the nightly feature min_type_alias_impl_trait. The trick is to create a type alias, and a dummy function from which the compiler can infer a defining use.
#![feature(min_type_alias_impl_trait)]
use tokio::net::TcpStream;
use core::future::Future;
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn __tcp_stream_connect_defining_use() -> TcpStreamConnectFut {
TcpStream::connect("127.0.0.1:8080")
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
This compiles, but does not work as expected:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
}
}
error[E0308]: mismatched types
--> src/lib.rs:18:35
|
6 | type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
| ------------------------------------------------ the expected opaque type
...
18 | self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
|
= note: while checking the return type of the `async fn`
= note: expected opaque type `impl Future` (opaque type at <src/lib.rs:6:28>)
found opaque type `impl Future` (opaque type at </playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.7.1/src/net/tcp/stream.rs:111:56>)
= help: consider `await`ing on both `Future`s
= note: distinct uses of `impl Trait` result in different opaque types
Using the dummy function we created does work:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(__tcp_stream_connect_defining_use());
}
}
So we can just create wrapper functions:
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
Except...
error: type parameter `A` is part of concrete type but not used in parameter list for the `impl Trait` type alias
--> src/main.rs:9:74
|
9 | fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
| __________________________________________________________________________^
10 | | TcpStream::connect(addr)
11 | | }
| |_^
We could just use String or &'static str, and the entire thing compiles:
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect(addr: &'static str) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
You can also add a generic parameter to the type alias itself, but that probably doesn't make sense in this case:
type TcpStreamConnectFut<A> = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut<A> {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut<&'static str>>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
So it is possible, but there are a couple of restrictions. I'm not sure how many of these are bugs, and how much of it is intentional behavior. There has been discussion about a typeof operator to make this easier, but this is what we've got at the moment.
You can just use a good old Vec, just like that:
use core::future::Future;
use tokio::net::TcpStream;
fn just_vec() -> Vec<impl Future<Output = std::io::Result<TcpStream>>> {
let mut v = Vec::new();
// connect to several streams
v.push(TcpStream::connect("127.0.0.1:8080"));
v.push(TcpStream::connect("127.0.0.2:8080"));
v
}
However, it gets more tricky if you want to store it in a struct, because unlike above, where concrete type can be inferred, you need to be more explicit with structs.
One stable way to do this, is to use a generic struct. This actually very similar to storing a closure (where you don't have a concrete type either).
use core::future::Future;
use tokio::net::TcpStream;
use tokio::io::AsyncWriteExt;
struct Foo<T> {
connections: Vec<T>,
}
/// This is just like any other vec-wrapper
impl<T> Foo<T> {
pub fn new() -> Self {
Self {
connections: Vec::new(),
}
}
pub fn push(&mut self, conn: T) {
self.connections.push(conn);
}
}
/// Some more specific functios that actually need the Future
impl<T> Foo<T> where T: Future<Output = std::io::Result<TcpStream>> {
pub async fn broadcast(self, data: &[u8]) -> std::io::Result<()> {
for stream in self.connections {
stream.await?.write_all(data).await?
}
Ok(())
}
}
async fn with_struct() -> std::io::Result<()> {
let mut foo = Foo::new();
// connect to several streams
foo.push(TcpStream::connect("127.0.0.1:8080"));
foo.push(TcpStream::connect("127.0.0.2:8080"));
// Do something with the connections
foo.broadcast(&[1,2,3]).await
}
I have many functions of the following type signature:
fn f() -> impl Fn(u32) -> u32 {
|x: u32| x
}
How can I give a name to Fn(u32) -> u32 so that I don't have to repeat it? Although I can do type X = Fn(u32) -> u32;, Rust will not let me use this because it is a type and not a trait. Must I wait for trait_alias or can I do something else?
You're exactly right. impl X requires X to be a trait, and it's impossible to have proper trait aliases until trait aliases land. When that happens you'll be able to do this:
#![feature(trait_alias)]
trait X = Fn(u32) -> u32;
fn f() -> impl X {
|x: u32| x
}
(playground)
Alternatively, when Permit impl Trait in type aliases lands, you'll be able to make impl trait a type alias. This is slightly different though. When we alias with type X = impl Trait, the compiler will ensure that every usage of X is actually the same concrete type. That would mean that, in your case, you wouldn't be able to use this with multiple different closures, since every closure has its own unique type.
#![feature(type_alias_impl_trait)]
type X = impl Fn(u32) -> u32;
fn f() -> X {
|x: u32| x
}
(playground)
However, this won't compile.
#![feature(type_alias_impl_trait)]
type X = impl Fn(u32) -> u32;
fn f() -> X {
|x: u32| x
}
// Even a closure with exactly the same form has a different type.
fn g() -> X {
|x: u32| x
}
The error is
error: concrete type differs from previous defining opaque type use
--> src/lib.rs:10:1
|
10 | / fn g() -> X {
11 | | |x: u32| x
12 | | }
| |_^ expected `[closure#src/lib.rs:7:5: 7:15]`, got `[closure#src/lib.rs:11:5: 11:15]`
|
note: previous use here
--> src/lib.rs:6:1
|
6 | / fn f() -> X {
7 | | |x: u32| x
8 | | }
| |_^
(playground)
This is in contrast to trait aliases, which would allow a different concrete type to be used with every function returning impl TraitAlias. See the RFCs that introduced this syntax and existential types in general for more.
Until one of those two features lands, you can get similar behavior to the trait alias with what is essentially a hack. The idea is to make a new trait which is essentially equivalent to the original trait, but has a shorter name.
// This trait is local to this crate,
// so we can implement it on any type we want.
trait ShortName: Fn(u32) -> u32 {}
// So let's go ahead and implement `ShortName`
// on any type that implements `Fn(u32) -> u32`.
impl<T: Fn(u32) -> u32> ShortName for T {}
// We can use `ShortName` to alias `Fn(u32) -> u32`.
fn f() -> impl ShortName {
|x: u32| x
}
// Moreover, the result of that can be used in places
// that expect `Fn(u32) -> u32`.
fn g<T: Fn(u32) -> u32>(x: &T) -> u32 {
x(6_u32)
}
fn main() {
// We only know that `x` implements `ShortName`,
let x = f();
// But we can still use `x` in `g`,
// which expects an `Fn(u32) -> u32` argument
let _ = g(&x);
}
(playground)
I've been experimenting with impl Trait and I came across this error when building a recursive function:
error[E0308]: if and else have incompatible types
--> src/main.rs:16:5
|
16 | / if logic {
17 | | one(false)
18 | | } else {
19 | | two()
20 | | }
| |_____^ expected opaque type, found a different opaque type
|
= note: expected type `impl Meow` (opaque type)
found type `impl Meow` (opaque type)
Here's the code to reproduce (Rust playground link):
trait Meow {
fn meow();
}
struct Cat(u64);
impl Meow for Cat {
fn meow() {}
}
fn one(gate: bool) -> impl Meow {
if gate {
one(false)
} else {
two()
}
}
fn two() -> impl Meow {
Cat(42)
}
fn main() {
let _ = one(true);
}
I haven't been able to find documentation about this particular issue and I find it odd that the compiler returns an error that roughly says "these two identical things are different".
Is there a way I can support the impl Trait syntax whilst doing this kind of recusion, please?
Disclaimer: this answer assumes that the reader understands that -> impl Trait requires a single type to be returned; see this question for returning different types.
Opacity
One of the core principles of Rust is that type-checking is entirely driven by the interface of functions, types, etc... and the implementation is ignored.
With regard to -> impl Trait functionality, this manifests by the language treating each -> impl Trait as an opaque type, solely identified by the function it comes from.
As a result, you can call the same function twice:
use std::fmt::Debug;
fn cat(name: &str) -> impl Debug { format!("Meow {}", name) }
fn meow(g: bool) -> impl Debug {
if g {
cat("Mario")
} else {
cat("Luigi")
}
}
fn main() {
println!("{:?}", meow(true));
}
But you cannot call different functions, even when they return the same type, if at least one is hidden behind -> impl Trait:
use std::fmt::Debug;
fn mario() -> impl Debug { "Meow Mario" }
fn luigi() -> &'static str { "Meow Luigi" }
fn meow(g: bool) -> impl Debug {
if g {
mario()
} else {
luigi()
}
}
fn main() {
println!("{:?}", meow(true));
}
Yields:
error[E0308]: if and else have incompatible types
--> src/main.rs:8:9
|
8 | / if g {
9 | | mario()
10 | | } else {
11 | | luigi()
12 | | }
| |_________^ expected opaque type, found &str
|
= note: expected type `impl std::fmt::Debug`
found type `&str`
And with two hidden behind -> impl Trait:
use std::fmt::Debug;
fn mario() -> impl Debug { "Meow Mario" }
fn luigi() -> impl Debug { "Meow Luigi" }
fn meow(g: bool) -> impl Debug {
if g {
mario()
} else {
luigi()
}
}
fn main() {
println!("{:?}", meow(true));
}
Yields the same error message than you got:
error[E0308]: if and else have incompatible types
--> src/main.rs:8:5
|
8 | / if g {
9 | | mario()
10 | | } else {
11 | | luigi()
12 | | }
| |_____^ expected opaque type, found a different opaque type
|
= note: expected type `impl std::fmt::Debug` (opaque type)
found type `impl std::fmt::Debug` (opaque type)
Interaction with Recursion
None.
The language does not special-case recursion here, and therefore does not realize that, in the case presented in the question, there is only ever one type involved. Instead, it notices fn one(...) -> impl Meow and fn two(...) -> impl Meow and concludes that those are different opaque types and therefore compile-time unification is impossible.
It may be reasonable to submit a RFC to tweak this aspect, either by arguing on the point of view of recursion, or by arguing on the point of view of module-level visibility; this is beyond the scope of this answer.
Work around
The only possibility is to ensure that the type is unique, and this requires naming it. Once you have captured the type in a name, you can consistently apply it everywhere it needs to match.
I'll refer you to #Anders' answer for his clever work-around.
I think an ideal compiler would accept your code, but the current language doesn’t allow for the recursive reasoning that would be needed to figure out that the types are actually the same in this case. You can work around this missing feature by abstracting over the impl Meow type with a type variable:
fn one_template<T: Meow>(gate: bool, two: impl FnOnce() -> T) -> T {
if gate {
one_template(false, two)
} else {
two()
}
}
fn one(gate: bool) -> impl Meow {
one_template(gate, two)
}
Rust playground link
I'm implementing tuple flattening for Rust. It requires converting
((A,B), (C, (D, E)), F)
into
Cons[
Cons[A, B, Nil],
Cons[
C, Cons[D, E, Nil], Nil
],
F,
Nil
]
I tried using specialization, but the compiler doesn't like it:
/// For non-tuple types.
impl<T> IntoCons for Val<T> {
default type Out = Cons<T, Nil>;
default fn into_cons(self) -> Cons<T, Nil> {
Cons {
head: self,
tail: Nil,
}
}
}
How can I do this? Any alternative that doesn't use unsafe is ok.
Complete example:
#![feature(specialization)]
use std::fmt::{Debug, Display};
pub trait Tr {
type It;
fn it(self) -> Self::It;
}
impl<T> Tr for T
where
T: Debug,
{
default type It = u8;
default fn it(self) -> Self::It {
0
}
}
impl<T> Tr for T
where
T: Debug + Display,
{
type It = u16;
fn it(self) -> Self::It {
0
}
}
fn main() {}
playground
Compiler output:
error[E0308]: mismatched types
--> src/main.rs:17:9
|
16 | default fn it(self) -> Self::It {
| -------- expected `<T as Tr>::It` because of return type
17 | 0
| ^ expected associated type, found integral variable
|
= note: expected type `<T as Tr>::It`
found type `{integer}`
The problem here is that you are returning Self::It but give it a 0. What happens if someone were to implement this with It being String? Since there is no way to prove that this is always going to be a number, you either need a trait bound or to change the method signature.
A possible way of doing it is like this:
pub trait Tr {
type It: Default;
fn it(self) -> Self::It;
}
impl<T> Tr for T
where
T: Debug,
{
default type It = u8;
default fn it(self) -> Self::It {
Default::default()
}
}
Playground Link