I am new to both Haskell and Rust and am attempting to translate Haskell code into Rust so as to compare the features that support genericity. However, it seems I either need help with syntax or some fundamental concepts in Rust.
I have this code in Haskell:
class HasEmpty a where
empty :: a
isEmpty :: a -> Bool
class Hashable a where
hash :: a -> Int
class (HasEmpty x, Hashable (Element x))
=> Hashset x where
type Element x
size :: x -> Int
The important part is at the bottom where we define a type class named Hashset which takes a single parameter x and sub-classes HasEmpty. The body of the type class defines an associated type, which is required to be Hashable, and an associated method size.
How can I do the same thing in Rust? Here is what I've written thus far:
trait HasEmpty {
fn empty(&self);
fn is_empty(&self) -> bool;
}
trait Hashable {
fn hash(&self) -> u32;
}
trait Hashset<E>
where E: Hashable
{
fn size(&self) -> i32;
}
This compiles and allows me to say that the associated type E is Hashable, but how can I 'inherit' the 'trait methods' of HasEmpty by using it as a supertrait? I know this is possible by saying:
trait Hashset: HasEmpty {
fn size(&self) -> i32;
}
but is it possible to both constrain the associated type and use a supertrait? Perhaps I am asking the wrong question.
is it possible to both constrain the associated type and use a supertrait?
Sure, I'm not sure why you didn't just combine the two syntaxes:
trait Hashset<E>: HasEmpty
where E: Hashable
{
fn size(&self) -> i32;
}
However, you should know that E is not an associated type, it's just a generic type. See When is it appropriate to use an associated type versus a generic type? for more info.
An associated type would be:
trait Hashset: HasEmpty {
type E: Hashable;
fn size(&self) -> i32;
}
Related
I have the following code (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
To me, it seems like this should compile as foo and bar have the same signature: fn(u32) -> bool. Yet, I get the following error:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
The same error can be triggered with this code:
let mut x = foo;
x = bar; // <-- error
I also tried to cast bar to the function pointer type again:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
This resulted in a slightly different error:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
I don't understand these errors at all. What are fn items vs. fn pointers and why are foo and bar different fn items?
Function item vs. function pointer
When you refer to a function by its name, the type you get is not a function pointer (e.g. fn(u32) -> bool). Instead, you get an a zero-sized value of the function's item type (e.g. fn(u32) -> bool {foo}).
The value is zero-sized because it doesn't store the actual function pointer. The type perfectly identifies the function, so there is no need to store actual data in the type. This has several advantages, mostly about easier optimizations. A function pointer is like you would expect from other languages: it stores an address to the function.
A function pointer refers to the function via stored address; a function item refers to the function via type information.
A function item can be coerced to a function pointer in many situations, for example: as argument to a function and in let _: fn(u32) -> bool = foo; statements. Additionally, you can explicitly cast a function item to a function pointer: foo as fn(u32) -> bool.
You can read more about this topic in the reference on function items, function pointers and coercion.
Solution to your problem
In your case, the compiler isn't smart enough to figure out that you want the function pointers from your foo and bar instead of function items. When you call call_both(foo, bar) the compiler sets the generic type F to be fn(u32) -> bool {foo}, because that's the type of the first argument. And then it complains that the second argument doesn't have the same type.
You can fix that by explicitly specifying the F parameter:
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
After specifying the type, the compiler can correctly coerce the arguments to a function pointer. You could also as-cast the first argument to fn(u32) -> bool explicitly.
You can fix your second example by explicitly stating the function pointer type, too:
let mut x: fn(u32) -> bool = foo;
x = bar;
In general: specifying the function pointer type somewhere to trigger the coercion will work.
Each named function has a distinct type since Rust PR #19891 was merged. You can, however, cast the functions to the corresponding function pointer type with the as operator.
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
It's also valid to cast only the first of the functions: the cast will be inferred on the second, because both functions must have the same type.
call_both(foo as fn(u32) -> bool, bar);
Suppose I have my own type as a tuple struct
struct MyType(u8);
And would like to implement the From trait for the other integral types in a generic way, like
impl From<T> for MyType {
fn from(num: T) -> Self {
MyType((num & 0xff) as u8)
}
}
Rust does not allow this because generic types T do not have the as operator. How can I constrain T to the integral types so that as can be used? something along the lines of
impl From<T: Integral> for MyType {...}
You can use the num-traits crate.
PrimInt is the relevant trait, which extends NumCast which allows conversion between the primitive numeric types.
EDIT: Here's an example implementation. It's a bit ugly because many of the relevant conversions return Option, since they don't know ahead of time that the values are in range.
impl<T: PrimInt> From<T> for MyType {
fn from(num: T) -> Self {
let clamped = num & T::from(0xFFu8).unwrap();
Self(clamped.to_u8().unwrap())
}
}
I have the following code (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
To me, it seems like this should compile as foo and bar have the same signature: fn(u32) -> bool. Yet, I get the following error:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
The same error can be triggered with this code:
let mut x = foo;
x = bar; // <-- error
I also tried to cast bar to the function pointer type again:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
This resulted in a slightly different error:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
I don't understand these errors at all. What are fn items vs. fn pointers and why are foo and bar different fn items?
Function item vs. function pointer
When you refer to a function by its name, the type you get is not a function pointer (e.g. fn(u32) -> bool). Instead, you get an a zero-sized value of the function's item type (e.g. fn(u32) -> bool {foo}).
The value is zero-sized because it doesn't store the actual function pointer. The type perfectly identifies the function, so there is no need to store actual data in the type. This has several advantages, mostly about easier optimizations. A function pointer is like you would expect from other languages: it stores an address to the function.
A function pointer refers to the function via stored address; a function item refers to the function via type information.
A function item can be coerced to a function pointer in many situations, for example: as argument to a function and in let _: fn(u32) -> bool = foo; statements. Additionally, you can explicitly cast a function item to a function pointer: foo as fn(u32) -> bool.
You can read more about this topic in the reference on function items, function pointers and coercion.
Solution to your problem
In your case, the compiler isn't smart enough to figure out that you want the function pointers from your foo and bar instead of function items. When you call call_both(foo, bar) the compiler sets the generic type F to be fn(u32) -> bool {foo}, because that's the type of the first argument. And then it complains that the second argument doesn't have the same type.
You can fix that by explicitly specifying the F parameter:
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
After specifying the type, the compiler can correctly coerce the arguments to a function pointer. You could also as-cast the first argument to fn(u32) -> bool explicitly.
You can fix your second example by explicitly stating the function pointer type, too:
let mut x: fn(u32) -> bool = foo;
x = bar;
In general: specifying the function pointer type somewhere to trigger the coercion will work.
Each named function has a distinct type since Rust PR #19891 was merged. You can, however, cast the functions to the corresponding function pointer type with the as operator.
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
It's also valid to cast only the first of the functions: the cast will be inferred on the second, because both functions must have the same type.
call_both(foo as fn(u32) -> bool, bar);
What Rust construct roughly accomplishes the same thing as the following OCaml?
type t = F : 'x * ('x -> string) -> t
let int_eg = F(1, string_of_int)
let str_eg = F("foo", fun x -> x)
let print x = print_string (match x with
| F(x,to_str) -> to_str x)
The closest thing you can get to existential types are trait objects:
// how ToString is declared
trait ToString {
fn to_string(&self) -> String;
}
let i32_str: Box<ToString> = Box::new(1);
let str_str: Box<ToString> = Box::new("foo");
fn print(value: &ToString) -> String {
value.to_string()
}
print_x(&i32_str); // automatically coerced from Box<ToString> to &ToString
print_x(&str_str);
With trait objects, the actual type is erased, and the only thing that remains is the knowledge that this particular value is of some type which implements the given trait. It is very similar to existential types with type class bounds in Haskell:
data Showable = Showable (forall a. Show a => a)
There is no way to bundle an arbitrary function with an arbitrary type, erasing it from the container signature, so you need to use a trait for it. Fortunately, traits are easy to define and implement for arbitrary types, so you can always define a trait and use a trait object. Trait objects cover almost all functionality for which existentials are usually needed in ML/Haskell.
Moreover, in many cases you don't need to work with trait objects at all! For example, the print() function above should actually be written as follows:
fn print<T: ToString>(value: &T) -> String {
value.to_string()
}
Such function is much more powerful because it works with arbitrary implementors of ToString trait, which include trait objects made out of ToString but also everything else which implements ToString. The only place you usually use trait objects is when you define heterogeneous data structures:
let many_to_strings: Vec<Box<ToString>> = vec![Box::new(1), Box::new("foo")];
But, as I said above, when you consume trait objects, in most cases you don't need to specify that you need a trait object - a regular generic function would be more idiomatic.
This is in the context of the question at Error message with unboxed closures. The answers point out that Rust generates a type each that is unique to each closure, since each of them can potentially capture a different set of variables from the enclosing scope(s).
Here is my question. The two different closure types in that FizzBuzz example are labelled differently, but look identical. How does the compiler resolve the closure type differences while still looking at the identical signatures of the type parameters?
This gap between what the compiler sees and what the programmer sees is confusing.
Thanks.
Edit: By the way, Rust Reference document Section 8.1.10 does not yet say anything about this.
Again, I want to start with the same example as in that answer. Compare this:
fn show_both_1<S: Show>(x: S, y: S) {
println!("{:?} {:?}", x, y);
}
and this:
fn show_both_2<S1: Show, S2: Show>(x: S1, y: S2) {
println!("{:?} {:?}", x, y);
}
(now using {:?} instead of {} because of the recent changes)
The first function requires that both arguments must have the same type, even though this type can be arbitrary as long as it implements Show:
show_both_1::<i32>(1i32, 2i32); // ok
show_both_1::<f64>(1.0f64, 2.0f64); // ok
show_both_1::<???>(1i32, 2.0f64); // not ok!
Obviously the last function call does not make sense, because types of the arguments are different, but the function wants them to have the same type. You can't even write the type parameter explicitly - should it be i32 or f64?
The second function allows different types, so all of these calls are ok:
show_both_2::<i32, i32>(1, 2);
show_both_2::<f64, f64>(1.0, 2.0);
show_both_2::<i32, f64>(1, 2.0);
Now for each argument a different type parameter is used, so it is perfectly fine to pass values of different types, as long as both of these types implement Show.
Absolutely the same thing happens with closures. For each closure the compiler generates a new unique type which implements one of Fn* traits. These types are anonymous, so you can't name them:
let f: ??? = |&: x: i32, y: i32| x + y;
There is nothing you can write instead of ??? above, but there is no need to because the compiler knows which type it has generated for the closure and so it can infer f's type. What really does matter is that this anonymous type will always implement one of special traits: Fn, FnMut or FnOnce. Consequently if you want your function to accept a closure, you need to pass it an instance of some type which implements one of these traits.
But this is natural job for generics! They are usually used when you want your function to accept some arbitrary type which implements some known trait, and situation with closures is absolutely the same. So you have this:
fn call_closure<F: FnMut(i64) -> bool>(f: F) -> bool {
f(10)
}
Because this function argument has generic type, this function can be used with any type which implements FnMut(i64) -> bool trait (which is just a shorthand for FnMut<(i64,), bool>, including anonymous types for closures generated by the compiler:
call_closure(|x| x > 10);
call_closure(|x| x == 42);
The compiler will generate a unique type for each of these closures, but since these generated types will implement FnMut(i64) -> bool trait, call_closure will happily accept both of them.
The situation with different type parameters which I described in the beginning naturally extends to closures because the same mechanism is used here, that is, traits.
fn call_closures_2<F: FnMut(i64) -> bool>(f1: F, f2: F) -> bool {
f1(10) && f2(20)
}
This function accepts two arguments which must be of the same type as long as this type implements FnMut(i64) -> bool trait. And this means that this invocation won't work:
call_closures_2(|x| x > 9, |x| x == 20)
It won't work because these closures have unique, i.e. different types, but the function requires that the types must be the same. For example, this does work:
fn call_closures_3<F: Fn(i64) -> bool>(f1: &F, f2: &F) -> bool {
f1(10) && f2(20)
}
let f = |&: x: i64| x == 10;
call_closures_3(&f, &f);
Note that the function arguments must still be of the same type (now references for the convenience of the example), but since we call it with references to the same closure, their type is the same, and everything works fine. This is not very useful though because it is very limiting - usually you want to provide different closures to functions which takes several ones.
For this reason the function needs separate type parameters in order to accept different closures:
fn call_closures_4<F1, F2>(f1: F1, f2: F2) -> bool
where F1: FnMut(i64) -> bool,
F2: FnMut(i64) -> bool {
f1(10) && f2(20)
}
call_closures_4(|x| x >= 9, |x| x <= 42)
Now type parameters are independent, and even though closures have different anonymous types, it is ok to call this function with them: F1 will become the generated type of the first closure and F2 will become the generated type of the second closure.
Every single closure that is written has a unique type. The compiler basically turns it into a structure with fields to store each variable that is closed over, and implements one of the Fn* traits (Fn, FnMut and FnOnce) for it with an appropriate signature, e.g. Fn(i64) -> bool, a.k.a. Fn<(i64,), bool>.
This is why it is using the regular generic constraint syntax, because that is exactly what is going on.
The word "signature" implies that the two closure types are the same, but they aren't. It's more useful to look at them as constraints. For reference:
fn fizzbuzz<F1, F2>(n: i64, f: F1, fs: &str, b: F2, bs: &str)
where F1: Fn(i64) -> bool,
F2: Fn(i64) -> bool
This doesn't say "F1 is the type Fn(i64) -> bool", it says: "F1 must be a type which implements the Fn(i64) -> bool signature". It's like how there are many different types which implement the Iterator trait, but which all implement the same interface.
In fact, things like Fn(i64) -> bool are actually just traits in disguise, but (if I recall correctly), the syntax for this isn't quite ready yet. In earlier versions, you could write this as Fn<(i64,), bool>.