4 ms·
The difference is that the author's suggestion as to what to do instead doesn't work in Rust. In Haskell there's no need to use a type class/trait here at all;
by zenhack 6y ago
The difference is that the author's suggestion as to what to do instead doesn't work in Rust. In Haskell there's no need to use a type class/trait here at all; you can just use a record. We could try to do the same thing in Rust with a struct:
struct EventHandler {
handle_start_event: fn(),
handle_completion_event: fn(),
}
...but this differs in a key way from the haskell example:
Rust's `fn` is not a closure, it's just a pointer to some code, and you can't construct them dynamically. If you want to pass around a _closure_, you need ...a trait object.
Something like:
struct EventHandler {
handle_start_event: Box<dyn impl Fn()>,
handle_start_event: Box<dyn impl Fn()>,
}
...but that's exactly the pattern the author of that post is suggesting getting away from.
I agree with the author's point, but it doesn't really apply to Rust because there isn't actually a simpler way to do it. And I think that's part of what the OP was getting at -- Trait objects are the closest thing Rust has to proper closures, and they're a bit awkward.
Note that I'm still something of a newbie at Rust, and don't feel like I can really say much about the day to day experience of using (or avoiding) trait objects due to limited experience.
- eptcyka 6y agoClosures are the closest thing to closures Rust has.
- zenhack 6y agoI was mostly talking about the type level; I certainly didn't mean to imply that Rust didn't have lambdas if that's what you're getting at. But there is no general type of Rust lambdas that take an A and return a B other than the trait object type `dyn impl Fn(A) -> B` (Or FnMut or FnOnce); the concrete type will depend on what variables it captures. Also, there's a lot of disagreement about exactly what the distinctions between `lambda`, `closure`, `anonymous function` and friends mean. The interpretation of `closure` I'm interested in is basically a bundle of code and data, which can be treated abstractly in a first class way. That could be something that appears as a lambda capturing variables in the text of your program or as an object in a more OO setting which, albeit more verbose for a single function, has a similar level of power of abstraction. Either way, part of that power is being able to write code that can work with the interface (irrespective of the shape of the enclosed data), and do things like store differing implementations in lists/vecs etc. The only mechanism Rust provides to do that is trait objects.
- gpderetta 6y ago> there is no general type of Rust lambdas that take an A and return a B other than the trait object type `dyn impl Fn(A) -> B` (Or FnMut or FnOnce); I don't understand the issue (and I know very little of rust), but isn't `dyn impl Fn(A) -> B` exactly that type? Any language with first class function parameters and objects, but type erases them (unlike rust and c++) would implement them pretty much like the dyn impl above under the hood.
- zenhack 6y agoThe comment I originally replied to linked to a blog post arguing that in Haskell, the practice of wrapping type classes (which Rust calls traits) in an existential is an antipattern, because rather than using fancy type system features, you could just use a record with some functions in it, which is simpler and more direct. But dyn impl is a trait wrapped in an existential. There's no concrete type a -> b, only a trait for types that can be 'called.' So to pass an arbitrary function around in a record/struct you need to do the very thing the post is saying is an antipattern (in Haskell). So the argument doesn't really apply to Rust because there isn't a simpler more direct way to achieve the same thing.
- gpderetta 6y agoRight, I completely failed to understand your point. You are completely right of course.
- dllthomas 6y agoGreat context, thanks! My intent was not to say "this is bad in Haskell so it might be bad here", but more to hope that suggested alternatives might prove interesting - particularly if running into limitations with the current approach.