Difficult to return Switch from a fn due to typing
KeatonTech opened this issue · 5 comments
Consider the following toy example:
fn foo(
switcher: Mutable<bool>,
on_true: Mutable<u8>,
on_false: Mutable<u8>
) -> ??? {
switcher.signal().switch(|val| if val {on_true.signal()} else {on_false.signal()})
}
The functionality works, but the return type is tricky to get right. The actual return type is Switch<MutableSignal<bool>, MutableSignal<u8>, [closure@my_project/my_file.rs:87:34: 87:72 on_true:_, on_false:_]>
, but due to the dependence on the internal closure type there's no way to actually tell Rust this. I've tried making the closure type into a generic but haven't had any luck with that. I suppose I could make a custom struct and manually implement Fn on it, but that's really cumbersome.
So, what if the function returns Box<dyn Signal<Item = u8>>>
? Well that actually does solve the problem! But it breaks a ton of stuff down the road because most of the SignalExt functions (including switch
) require Self
to be Sized
, which is not true of dyn Signal
.
I appreciate that futures_signals tries so hard to avoid indirection by sizing things, but it leads to some really incredibly long type signatures in complex use cases. Even if I do convert all my closures to structs, having everything so tightly typed prevents a lot of reuse. For example, if I wanted on_true
and on_false
to be different types (maybe one is a Switch
and one is a Mutable
), the return signature would have to be Switch<MutableSignal<bool>, dyn Signal<Item=u8>, MyCustomClosureStruct>
-- and then I'd just run into the sizing problem again down the road. I'm wondering if it would be a good idea to have something like BoxSignal
which wraps dyn Signal
but gives it a size (I tried this myself but ran into problems with all the pinning).
You should be able to return impl Signal<Item = u8>
, does that not work?
Also note that this issue isn't because of futures-signals
... trait methods which accept self
must be Sized
, Rust mandates it. And the inability to call Sized
methods on a dyn Trait
is also a limitation within Rust itself, not futures-signals
.
The best option is to use impl Signal<Item = u8>
. But if you need dynamic types then you must use Box<dyn Signal<Item = u8> + Unpin>
instead. That works because there is an explicit impl for it.
Seems to work, thanks!
Also, you mentioned returning different types within switch
, this is how you would do that:
fn foo(
switcher: Mutable<bool>,
on_true: Mutable<u8>,
on_false: Mutable<u8>
) -> impl Signal<Item = u8> {
switcher.signal().switch(move |val| -> Box<dyn Signal<Item = u8> + Unpin> {
if val {
Box::new(on_true.signal())
} else {
Box::new(on_false.signal())
}
})
}
Note that this is only needed if on_true
and on_false
are different types.
Also note that the function still returns impl Signal<Item = u8>
, because even though the closure returns different types, the switch
itself is still a single type, so the switch
does not need the overhead of Box
.