Constrain Node as: From<PartialNode>
yoshuawuyts opened this issue · comments
Feature Request
Summary
Constrain the HashMethods::Node
type as Node + From<PartialNode>
, and remove the node
method.
Motivation
If we can constrain the Node
type in HashMethods
to be From<PartialNode>
, we can remove an extra method, and remove a piece of boilerplate required for setting up DefaultNode
. It also creates a more Rusty API.
Expected Behavior
Define HashMethods::Node
as:
pub trait HashMethods {
type Node = Node + From<PartialNode>;
}
and remove fn node();
from the trait.
Drawbacks
A custom Node
implementation now requires a trait to be implemented, which might be a bit confusing for people new to Rust. But it'll result in less work for people using DefaultNode
, and a slight API change for people using custom Node
implementations.
Unresolved Questions
None.
I'm happy to spend some time this week on this enhancement idea. I'm still a little new to Rust, so I'll reach out when I have questions or need some guidance.
@scotttrinh fantastic! 🎉
I've spent a little time here, but I'm having a hard time figuring out a generic way to impl From<PartialNode> for DefaultNode
without taking the hash
as an argument. Any direction you can point me in?
@scotttrinh dang yeah, actually I think you might be right! -- not sure it's possible :(
Oh actually: we could make this happen is if PartialNode
had an Option<Hash>
field. Because all information is then contained in the struct, converting using From<>
would work!
Or alternatively: we could create a new struct HashPair
that contains both a PartialNode
and a Hash
, which is only ever used as an internal type unless you want to implement your own Node
type.
DefaultNode
would then have a From<HashPair>
impl.
A few questions to consider:
- Would this work for tuples? Would that be better?
- What should we call this type?
HashPair
doesn't mentionNode
at all.PartialNodeWithHash
would be more accurate, but doesn't exactly roll of the tongue. Suggestions? - Should this be part of any
prelude
? We should probably introduce a prelude at this point, haha.
Fwiw, it looks like it's possible to convert from a tuple: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=2c4ed43b253a06a357c92dd3a7e2724d.
Although to be honest I think a struct might be a lot cleaner. We'd just have to name it though.
Some other struct name ideas:
NodeParts
PartialHashPair
PartialWithHash
Coming from the other side: What about making a FromPartial
trait like what is already implemented in DefaultNode
with the from_partial
method? I believe we can still define Into
. Are there any advantages with using the standard From
trait that we wouldn't get by implementing our own?
Ohhh, I like NodeParts
a lot! It describes what a Node is pretty well I think!
Are there any advantages with using the standard From trait that we wouldn't get by implementing our own?
Excellent question! I have a few thoughts:
type Node
already needs to implementtrait Node
. I don't think there's currently any scenario where a type needs to implement one, but not the other.- In which case we'd probably have a
from_partial
method required on theNode
. I already kind of feel like ourNode
trait is more of a grab bag of methods, more than a coherent interface. This would add to that. - People in the wider Rust ecosystem are used to having conversion traits exist as
From
, or now also the newerTryFrom
traits. For examplestd::str::FromStr
exists, but it feels a bit more redundant now thatTryFrom
basically implements the same behavior, but works on every type. - Which feels like it ties into a wider point: it seems most of the Rust ecosystem is converging on implementing
TryFrom
as the "works everywhere solution" to converting between types, which in turn can call down toTry
,FromStr
and others if it can also be converted without errors. I think it would make a lot of sense for us to not introduce any new abstractions, but instead stick to the standard conventions as much as possible.
Does that reasoning make sense?
It was pointed out on Twitter that there's prior art for implementing From
for a tuple: https://github.com/hyperium/hyper/blob/master/examples/hello.rs#L13. Whether that's a good idea still remains a question, haha.
It was pointed out on Twitter that there's prior art for implementing
From
for a tuple: hyperium/hyper:examples/hello.rs@master#L13.
Followed that rabbit trail a little and it looks like they named that tuple pieces
in std::net::SocketAddr
impl for Into/From
.
I think it would make a lot of sense for us to not introduce any new abstractions, but instead stick to the standard conventions as much as possible.
I 100% agree that following language convention is the way to go. 👍 If it comes down to tuple vs. struct, maybe it's worth looking around a bit more for other examples?
Random thought: Is there a pattern in the community of implementing From
for both? Should be easy to do. In the cases where it improves readability or construction, you can use the struct, otherwise just inline the tuple?
In the cases where it improves readability or construction, you can use the struct, otherwise just inline the tuple?
Hmm, I don't think that'd work here. We'd have to constrain the type parameter as Node + From<Pieces> + From<(PartialNode, Self::Hash)>
to accept all variants. What we could do is:
impl From<(PartialNode, T)> for Pieces
or something. But at that point I kind of wonder if it's worth it. I think we should probably pick one of the other, and I'm personally more inclined to having a named struct here.