Is it possible/reasonable to (effectively) impl <T: Display> IntoDynNode for Option<T>
?
#2870
EndilWayfare
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Preamble
First of all, love what you're doing here. I very recently came to
dioxus
0.5 from several years ofyew
. I got tired of fighting tooth-and-nail to avoid clones, extra comparisons, and unnecessary re-renders. The "it's almost entirely static except for exactly where it isn't" paradigm is a game changer.There are a lot of things that
dioxus
can accomplish more quickly, cleanly, and compactly, but there are still some rough edges where I find myself writing boilerplate or awkward constructions.It's very possible I just haven't been around long enough to pick up necessary/useful idioms yet, but I have some questions about potential design decisions. Given the technical depth and complexity of this workspace, it's hard for me to quickly determine if my "why doesn't it do it this way" is a "because it turns out that's actually just a bad idea" thing, a "the compiler already does that/that's not actually meaningfully more efficient/perhaps because WASM quirks" thing, or a "we just didn't, hmm that's an interesting idea" thing.
Ok, The Actual Question
Many times, I have a data atom
T
whereT: Display
(because I want to render it sometimes) but I prefer to store/pass/operate-on it as aT
and not as aString
until the very end, right when Irsx!("{t}")
it into the page. AsT
, that's straightforward enough; the overlap betweenrsx!
andformat!
+friends makes it easy and familiar.But not when it's an
Option<T>
. BecauseOption<T>: !Display
even whenT: Display
, which is the sane default in most Rust programs. However, in the case of UI rendering, I think it's reasonable to say that almost always the behavior you want is the equivalent ofObviously, the biggest technical roadblock to
impl<T: Display> IntoDynNode for Option<T>
is the limitations of the trait system itself. That blanket-impl couldn't coexist with the existingimpl<T: IntoDynNode> for Option<T>
. But that's a very "X/Y Problem" kind of thing. Really, the trait isn't what I care about , it's just a means to the "please god can i somehow avoid typing|t| t.to_string()
it burns" end.It may seem a bit petty, but this primarily crops up for me a lot in more higher-order-component-like, reusable-behavior, abstraction-because-come-on-abstractions-rule situations where extra noise can make it even harder to scan/grok what's going on at a glance.
Let's Get a Bit More Concrete
For the purpose of discussion, let's imagine we have some types something like these:
It would be ideal if we could just
instead of
On its own, in isolation, it's not that bad. It's in already-noisy or highly-repetitive contexts where it starts becoming more of an issue.
It makes me feel like I'm doing something wrong, when I think this is about as clear/correct/elegant as you're going to get in that situation.
yew
has a similar problem, honestly.For some reason, I (subjectively) have less of a revulsion to
.map(Html::from)
than to either.map(|t| t.to_string())
or.as_ref().map(ToString::to_string)
.The "single method-call wrapper" closure feels too ad-hoc, or like a hack.
The "to string to string" feels transparently, redundantly repetitive. Not to mention, requiring
as_ref
because of the trait method signature.Why
IntoDynNode
as a Separate, Explicit Trait?I'm certain there is a good, thoughtful reason. I'm just not sure what exactly it is, and I feel like it would give me a better "gut feel" for how I'm supposed to reason about the
dioxus
codebase if I knew.Is it for clarity of intent? Is it to separate value-transformation/conversion from "static monomorphized dynamic dispatch" semantics? Is it a publicly-namable "semi-sealed" trait? Is it a proc-macro thing, perhaps related to expansion-time type resolution limitations?
yew
has a very similar trait,ToHtml
. It seems to, similarly, be more about "type-plumbing" than about "formatting transformation", and to be specifically about "curly-brace expansion in macros". However, there are two main things it has additionally:ToHtml
impls for Rust primitive values. Contrarily, you can'trsx!({42})
, you have torsx!("{42}")
. Not necessarily relevant, just interesting.impl<T: ToString> From<T> for VNode
, which is whyHtml::from
is a thing. (Html
is just a type alias toVNode
.)What would be the downside of
impl<T: ToString> From<T> for DynamicNode
?That wouldn't get us all the way to "
Option<T>
s just work", but it would get us to "about on-par withyew
", which would be an improvement, in my opinion.yew
has the "advantage" of only having a single "virtual DOM node" type, instead of the static/dynamic distinction that makesdioxus
more powerful. So, the question is, might that get us into trouble, somehow?Are there cases where a formatted value could be a static node? Where it can't change value over the lifecycle of a template? (Is that even possible currently, in userland, outside of the
rsx
macro? I don't know enough about the internals.) And so it could be a mistake to prematurely couple "from ToString" to "is dynamic"?Would it be "too magical", or cause type-inference issues that I'm not foreseeing?
If
From<T: ToString>
, why not further?The trick seems to be (at a minimum) avoiding the "upstream crates may add a new impl of trait
std::fmt::Display
for typestd::option::Option<_>
in future versions" conflicting implementation error. That, and the blanketimpl<T> From<T> for T
.Back-of-the-envelope type-checking seems to suggest that something along these lines would technically compile:
I know I would do this, because I have a bit of a tendency to go full "type astronaut" and get entirely too carried away with clever-sounding constructions. That doesn't necessarily mean that it's a good idea.
Alternate Solution I Guess
Just add another trait!
Beta Was this translation helpful? Give feedback.
All reactions