How to write a function that takes a slice of functions? - function

I am trying to write a function that takes a slice of functions. Consider the following simple illustration:
fn g<P: Fn(&str) -> usize>(ps: &[P]) { }
fn f1() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn f2() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn main() {
g(&[f1(), f2()][..]);
}
It fails to compile:
error[E0308]: mismatched types
--> src/main.rs:6:15
|
6 | g(&[f1(), f2()][..]);
| ^^^^ expected opaque type, found a different opaque type
|
= note: expected type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
found type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
Is there any way to do this?

Your problem is that every element of the array must be of the same type, but the return of a function declared as returning impl Trait is an opaque type, that is an unspecified, unnamed type, that you can only use by means of the given trait.
You have two functions that return the same impl Trait but that does not mean that they return the same type. In fact, as your compiler shows, they are different opaque types, so they cannot be part of the same array. If you were to write an array of values of the same type, such as:
g(&[f1(), f1(), f1()]);
then it would work. But with different functions, there will be different types and the array is impossible to build.
Does that mean there is no solution for your problem? Of course not! You just have to invoke dynamic dispatch. That is you have to make your slice of type &[&dyn Fn(&str) -> usize]. For that you need to do two things:
Add a level of indirection: dynamic dispatching is always done via references or pointers (&dyn Trait or Box<dyn Trait> instead of Trait).
Do an explicit cast to the &dyn Trait to avoid ambiguities in the conversion.
There are many ways to do the cast: you can cast the first element of the array, or you can declare the temporary variables, or give the slice a type. I prefer the latter, because it is more symmetric. Something like this:
fn main() {
let fns: &[&dyn Fn(&str) -> usize] =
&[&f1(), &f2()];
g(fns);
}
Link to a playground with this solution.

Related

Functions as the struct fields or as struct methods

Could anybody help me to clarify in which situations is better to use functions as the struct fields and when as the methods of struct?
A field of function type is not a method, so it's not part of the method set of the struct type. A "true" method declared with the struct type as the receiver will be part of the method set.
That being said, if you want to implement an interface, you have no choice but to define "true" methods.
Methods are "attached" to concrete types and cannot be changed at runtime. A field of function type may be used to "mimic" virtual methods, but as said above, this is not a method. A field of function type may be reassigned at runtime.
Like in this example:
type Foo struct {
Bar func()
}
func main() {
f := Foo{
Bar: func() { fmt.Println("initial") },
}
f.Bar()
f.Bar = func() { fmt.Println("changed") }
f.Bar()
}
Which outputs (try it on the Go Playground):
initial
changed
Fields of function type are often used to store callback functions. Examples from the standard lib are http.Server and http.Transport.

How to return concrete type from generic function?

In the example below the Default trait is used just for demonstration purposes.
My questions are:
What is the difference between the declarations of f() and g()?
Why g() doesn't compile since it's identical to f()?
How can I return a concrete type out of a impl trait generically typed declaration?
struct Something {
}
impl Default for Something {
fn default() -> Self {
Something{}
}
}
// This compiles.
pub fn f() -> impl Default {
Something{}
}
// This doesn't.
pub fn g<T: Default>() -> T {
Something{}
}
What is the difference between the declarations of f() and g()?
f returns some type which implements Default. The caller of f has no say in what type it will return.
g returns some type which implements Default. The caller of g gets to pick the exact type that must be returned.
You can clearly see this difference in how f and g can be called. For example:
fn main() {
let t = f(); // this is the only way to call f()
let t = g::<i32>(); // I can call g() like this
let t = g::<String>(); // or like this
let t = g::<Vec<Box<u8>>(); // or like this... and so on!
// there's potentially infinitely many ways I can call g()
// and yet there is only 1 way I can call f()
}
Why g() doesn't compile since it's identical to f()?
They're not identical. The implementation for f compiles because it can only be called in 1 way and it will always return the exact same type. The implementation for g fails to compile because it can get called infinitely many ways for all different types but it will always return Something which is broken.
How can I return a concrete type out of a impl trait generically typed declaration?
If I'm understanding your question correctly, you can't. When you use generics you let the caller decide the types your function must use, so your function's implementation itself must be generic. If you want to construct and return a generic type within a generic function the usual way to go about that is to put a Default trait bound on the generic type and use that within your implementation:
// now works!
fn g<T: Default>() -> T {
T::default()
}
If you need to conditionally select the concrete type within the function then the only other solution is to return a trait object:
struct Something;
struct SomethingElse;
trait Trait {}
impl Trait for Something {}
impl Trait for SomethingElse {}
fn g(some_condition: bool) -> Box<dyn Trait> {
if some_condition {
Box::new(Something)
} else {
Box::new(SomethingElse)
}
}
how can I return a concrete type out of a "impl trait" generically typed declaration?
By "impl trait" generically typed declaration I presume you mean "impl trait" rewritten to use named generics. However, that's a false premise - impl Trait in return position was introduced precisely because you can't express it using named generics. To see this, consider first impl Trait in argument position, such as this function:
fn foo(iter: impl Iterator<Item = u32>) -> usize {
iter.count()
}
You can rewrite that function to use named generics as follows:
fn foo<I: Iterator<Item = u32>>(iter: I) -> usize {
iter.count()
}
Barring minor technical differences, the two are equivalent. However, if impl Trait is in return position, such as here:
fn foo() -> impl Iterator<Item = u32> {
vec![1, 2, 3].into_iter()
}
...you cannot rewrite it to use generics without losing generality. For example, this won't compile:
fn foo<T: Iterator<Item = u32>>() -> T {
vec![1, 2, 3].into_iter()
}
...because, as explained by pretzelhammer, the signature promises the caller the ability to choose which type to return (out of those that implement Iterator<Item = u32>), but the implementation only ever returns a concrete type, <Vec<u32> as IntoIterator>::IntoIter.
On the other hand, this does compile:
fn foo() -> <Vec<u32> as IntoIterator>::IntoIter {
vec![1, 2, 3].into_iter()
}
...but now the generality is lost because foo() must be implemented as a combination of Vec and into_iter() - even adding a map() in between the two would break it.
This also compiles:
fn foo() -> Box<dyn Iterator<Item = u32>> {
Box::new(vec![1, 2, 3].into_iter())
}
...but at the cost of allocating the iterator on the heap and disabling some optimizations.

Missing implementation for top level functions

I am trying to make a trait usable on top-level functions in Rust.
trait FnTrait {
fn call(self);
}
impl FnTrait for fn() {
fn call(self) {
self()
}
}
fn foo() {
println!("Hello, World!")
}
fn main() {
FnTrait::call(foo)
}
However the code below fails to compile with (Playground Link)
error[E0277]: the trait bound `fn() {foo}: FnTrait` is not satisfied
--> <anon>:16:5
|
16 | FnTrait::call(foo)
| ^^^^^^^^^^^^^ the trait `FnTrait` is not implemented for `fn() {foo}`
|
= help: the following implementations were found:
<fn() as FnTrait>
= note: required by `FnTrait::call`
I found I can trick it into compiling by casting foo like so
FnTrait::call(foo as fn())
But it is annoying and some of the functions in my program are more complicated than foo. Any way to avoid the cast? Is my trait wrong somehow?
Every function in Rust has its own type. As you can see, foo isn't a fn(), it's a fn() {foo}; sadly, this is not an actual type you can write in source, that's just a compiler message thing. The distinction exists to make it easier for the compiler to let you pass around functions as values whilst still being able to inline the calls.
The consequence is that named functions pointers cannot be turned into general function pointers without either a cast or a type hint. For example, this works:
fn foo() {
println!("Hello, World!")
}
fn bar(f: fn()) {
f()
}
fn main() {
bar(foo)
}
However, I'm not aware of any way to leverage this to get the trait to work.
The only way forward is to stop trying to implement the trait for function pointers, and instead implement it for everything callable:
trait FnTrait {
fn call(self);
}
impl<F> FnTrait for F where F: FnOnce() {
fn call(self) {
self()
}
}
fn foo() {
println!("Hello, World!")
}
fn main() {
foo.call();
}
(Semi-relevant answer about the difference between Fn, FnMut and FnOnce.)
This will work for anything that's callable with that signature, including both functions and closures. The downside is that you can only have one such implementation. You can't implement this trait for any other signature.
One generic implementation, or many specific implementations and lots of manual casting. Pick your poison.
As an aside: there's no such thing as a "top level function" in Rust, at least not as a thing distinct from other kinds of functions. Functions are functions, no matter where they appear. Instance functions a.k.a. methods are still regular functions, it's just that their first argument is called "self".

How to write a Rust function that takes an iterator?

I'd like to write a function that accepts an iterator and returns the results of some operations on it. Specifically, I'm trying to iterate over the values of a HashMap:
use std::collections::HashMap;
fn find_min<'a>(vals: Iterator<Item=&'a u32>) -> Option<&'a u32> {
vals.min()
}
fn main() {
let mut map = HashMap::new();
map.insert("zero", 0u32);
map.insert("one", 1u32);
println!("Min value {:?}", find_min(map.values()));
}
But alas:
error: the `min` method cannot be invoked on a trait object
--> src/main.rs:4:10
|
4 | vals.min()
| ^^^
error[E0277]: the trait bound `std::iter::Iterator<Item=&'a u32> + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:3:17
|
3 | fn find_min<'a>(vals: Iterator<Item = &'a u32>) -> Option<&'a u32> {
| ^^^^ `std::iter::Iterator<Item=&'a u32> + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=&'a u32> + 'static`
= note: all local variables must have a statically known size
error[E0308]: mismatched types
--> src/main.rs:11:41
|
11 | println!("Min value {:?}", find_min(map.values()));
| ^^^^^^^^^^^^ expected trait std::iter::Iterator, found struct `std::collections::hash_map::Values`
|
= note: expected type `std::iter::Iterator<Item=&u32> + 'static`
found type `std::collections::hash_map::Values<'_, &str, u32>`
I get the same error if I try to pass by reference; if I use a Box, I get lifetime errors.
You want to use generics here:
fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
I: Iterator<Item = &'a u32>,
{
vals.min()
}
Traits can be used in two ways: as bounds on type parameters and as trait objects. The book The Rust Programming Language has a chapter on traits and a chapter on trait objects that explain these two use cases.
Additionally, you often want to take something that implements IntoIterator as this can make the code calling your function nicer:
fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
I: IntoIterator<Item = &'a u32>,
{
vals.into_iter().min()
}
Since Rust 1.26 impl Trait are available. Here is a more compact version using impl Trait.
use std::collections::HashMap;
fn find_min<'a>(vals: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
vals.min()
}
fn main() {
let mut map = HashMap::new();
map.insert("zero", 0u32);
map.insert("one", 1u32);
println!("Min value {:?}", find_min(map.values()));
}
playground
This behaviour is a little unintuitive from those with a Python background rather than, say, a C++ background, so let me clarify a little.
In Rust, values are conceptually stored inside the name that binds them. Thus, if you write
let mut x = Foo { t: 10 };
let mut y = x;
x.t = 999;
y.t will still be 10.
So when you write
let x: Iterator<Item=&'a u32>;
(or the same in the function parameter list), Rust needs to allocate enough space for any value of type Iterator<Item=&'a u32>. Even if this was possible, it wouldn't be efficient.
So what Rust does instead is offer you the option to
Put the value on the heap, eg. with Box, which gives Python-style semantics. Then you can take generically with &mut Iterator<Item=&'a u32>.
Specialize each function invocation for each possible type to satisfy the bound. This is more flexible, since a trait reference is a possible specialization, and gives the compiler more opportunities for specialization, but means you can't have dynamic dispatch (where the type can vary dependent on runtime parameters).

the impl does not reference any types defined in this crate

In one of my structs Struct1 there's a field of type time::Tm. Somewhere in the code an instance of the struct gets decoded from a json string to an instance of Struct1.
json::decode(&maybe_struct1)
At first I got an error saying that to_json isn't implemented for time::Tm. So I implemented it:
impl json::ToJson for time::Tm {
fn to_json(&self) -> json::Json {
let mut d = BTreeMap::new();
d.insert("tm_sec".to_string(), self.tm_sec.to_json());
d.insert("tm_min".to_string(), self.tm_min.to_json());
//..............
And now it says
error: the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
I gathered that it was a bug before version 1. But it is still? If not how do I fix it?
It's not a bug - it is feature. Really. To implement trait any of this statements must be true:
type is declared in this module
trait is declared in this module
otherwise you get E0117. You can find more info using rustc --explain E0117:
This error indicates a violation of one of Rust's orphan rules for trait implementations. The rule prohibits any implementation of a foreign trait (atrait defined in another crate) where
the type that is implementing the trait is foreign
all of the parameters being passed to the trait (if there are any) are also foreign.
Here's one example of this error:
impl Drop for u32 {}
To avoid this kind of error, ensure that at least one local type is referencedby the impl:
pub struct Foo; // you define your type in your crate
impl Drop for Foo { // and you can implement the trait on it!
// code of trait implementation here
}
impl From<Foo> for i32 { // or you use a type from your crate as
// a type parameter
fn from(i: Foo) -> i32 { 0 }
}
Alternatively, define a trait locally and implement that instead:
trait Bar {
fn get(&self) -> usize;
}
impl Bar for u32 {
fn get(&self) -> usize { 0 }
}
For information on the design of the orphan rules, see RFC 1023.
EDIT:
To achieve what you want you have 2 solutions:
implement ToJson on your whole type:
impl json::ToJson for Struct1 { … }
create a wrapper type struct TmWrapper(time::Tm); and implement 2 traits for it From and ToJson.
EDIT 2:
Step by step explonation:
This is what you want to achieve: http://is.gd/6UF3jd
Solutions:
implement trait on whole type using types that you want: http://is.gd/CRfPeJ
create wrapper type and implement trait that you want on it: http://is.gd/7XV5w9
This is exactly what is described in explanation of the error code above.
So you see - at least one of trait or struct must be declare within current unit to allow implementation of trait on given type. In your case both of them are external types and that is what is Rust preventing. If you want to achieve something like that you need to use some hacks as described above.
It's not a bug. The language requires when you implement a trait that you defined either the trait or the type involved. If you implemented neither, it doesn't allow it.
If it didn't, it would be possible for someone else to come along and also implement json::ToJson for time::Tm, and suddenly the compiler has no idea which code to use.
The simplest way to work around this is to wrap your time::Tm in a newtype, like so:
struct TmWrap(time::Tm);
Now, because you defined this type, you can implement json::ToJson on it. This does mean you have to wrap/unwrap the Tm constantly, but the only other alternative is to implement ToJson for the entire containing Struct1 type, which is probably going to be even more work.