rust: If an enum has all variants derived from a generic struct, what's the best way to inspect the generic stored in the enum? - enums

I've condensed things down to where I have a Generic Adapter, which is stored in an enum, that is non-generic. This looks like:
enum Collective {
StateTable(Adapter<StateTable>),
TestMessage(Adapter<TestMessage>)
}
I have getters and setters for Adapter, and I've stored Collective values in vector. While I can use match, to crack things out, via the following, I'm wondering if there isn't a better way.
impl Collective {
fn get_id(&self) -> u128 {
match self {
ReceiverAdapters::StateTable(a) => a.get_id(),
ReceiverAdapters::TestMessage(a) => a.get_id(),
}
}
I've considered From, but not sure if that will help. I'm also concerned with maintenance, as the number of variants grows. Can anyone provide an alternate to match? Or is match, under the covers compiling down to near-zero code in this case?

Related

In rust, what's the idiomatic way of expressing a struct that can be ordered, but only in reference to a standard value?

I'm trying to implement a game similar to GeoGuessr, where players enter geographic coordinates according to a street-view image, and are ranked by their distance to the correct location of the image.
I need a data structure to represent the submission of a player, and I want it to implement PartialEq and PartialOrd so that it can be easily sorted within container structures. However, unlike ordinary PartialOrd structures that are comparable by themselves, my structure is only comparable in reference to the correct answer.
I would like the rankings to be accessible at any time, so I'd prefer a container that always maintains the order of its elements to avoid the sorting costs, in my case I chose skiplist::ordered_skiplist::OrderedSkipList. That means methods like sort_by_key are unavailable to me, and I have to implement PartialOrd for my structure.
So I ended up keeping a reference to the correct answer as a field in my structure:
struct Submission<'a> {
submitted: Location,
correct: &'a Location,
}
impl Submission<'_> {
fn distance(&self) -> f64 {
self.submitted.distance(*self.correct)
}
}
impl PartialEq for Submission<'_> {
fn eq(&self, other: &Self) -> bool {
let d1 = self.distance();
let d2 = other.distance();
d1.eq(&d2)
}
}
impl PartialOrd for Submission<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
let d1 = self.distance();
let d2 = other.distance();
d1.partial_cmp(&d2)
}
}
But this doesn't seem idiomatic to me, as it doesn't restrict comparison between Submissions with different correct references, which would be invalid. Also, maintaining the same correct reference in each Submission seems a redundant cost. Is there a more idiomatic way of defining the data structure for this scenario?
Edit:
I've considered comparing the correct references in partial_cmp and returning None for invalid comparisons, but that's also redundant for my case as I can prevent this in coding myself. I'm looking for a compile-time way of preventing invalid comparisons, rather than a runtime one.

Polymorphism on structs without methods in Go

I'm working on several web server projects in Go, and there is a common problem that I'm always facing. I know we can achieve something like polymorphism in Go with interfaces and methods, but many times I had a scenario that I needed polymorphism on some data-holder structs that (maybe) just had some common fields, and no methods at all.
For example consider a story writing platform, where each user can write short stories and novels:
type ShortStory struct {
Name string
ID int
Body string
}
type LongStory struct {
Name string
ID int
Chapters []string
}
Now I simply want to have a data layer function, say GetStories(), which fetches all stories written by a user from database.
func GetStories(id int) []SOME_TYPE {
...
}
There are really no methods that I want to have on my ShortStory and LongStory structs. I know I can add a dummy method and let them satisfy some Storier interface, then use that interface as return type. But since there is no method I would want on a data container model, adding a dummy method just for the language to enable a feature, seems like a poor design choice to me.
I can also make the function return []interface{}, but that's against the whole idea of "typed language" I believe.
Another way is to have two separate GetShortStories() and GetLongStories() methods, which return a slice of their own type. But at some point I would finally want to merge those two slices into one and there I would again need a []interface{}. Yes, I can return a JSON like:
{
"short_stories" : [...],
"long_stories" : [...]
}
But I want my json to be like:
[{...}, {...}, {...}]
And I wouldn't change my APIs because of a language's limits!
I'm not a pro in Go, so am I missing something here? Is there a Go-ish approach to this, or is it really bad language design on Golang's side?
If you cannot express what you want to do using the features of a language, you should first try to change the way you structure your program before blaming the language itself. There are concepts that cannot be expressed in Go but can be expressed well in other languages, and there are concepts you cannot express well in other languages but you can in Go. Change the way you solve the problem to effectively use the language.
One way you can address your problem is using a different type of struct:
type Story struct {
Name string
ID int
ShortBody string
Chapters []string
}
If the Chapters is empty, then it is a short story.
Another way:
type Story struct {
Name string
ID int
Content StoryContent
}
type StoryContent interface {
Type() string
}
type ShortStory interface {
StoryContent
Body() string
}
type LongStory interface {
StoryContent
Chapters() []string
}
etc.

Read from an enum without pattern matching

The Rust documentation gives this example where we have an instance of Result<T, E> named some_value:
match some_value {
Ok(value) => println!("got a value: {}", value),
Err(_) => println!("an error occurred"),
}
Is there any way to read from some_value without pattern matching? What about without even checking the type of the contents at runtime? Perhaps we somehow know with absolute certainty what type is contained or perhaps we're just being a bad programmer. In either case, I'm just curious to know if it's at all possible, not if it's a good idea.
It strikes me as a really interesting language feature that this branch is so difficult (or impossible?) to avoid.
At the lowest level, no, you can't read enum fields without a match1.
Methods on an enum can provide more convenient access to data within the enum (e.g. Result::unwrap), but under the hood, they're always implemented with a match.
If you know that a particular case in a match is unreachable, a common practice is to write unreachable!() on that branch (unreachable!() simply expands to a panic!() with a specific message).
1 If you have an enum with only one variant, you could also write a simple let statement to deconstruct the enum. Patterns in let and match statements must be exhaustive, and pattern matching the single variant from an enum is exhaustive. But enums with only one variant are pretty much never used; a struct would do the job just fine. And if you intend to add variants later, you're better off writing a match right away.
enum Single {
S(i32),
}
fn main() {
let a = Single::S(1);
let Single::S(b) = a;
println!("{}", b);
}
On the other hand, if you have an enum with more than one variant, you can also use if let and while let if you're interested in the data from a single variant only. While let and match require exhaustive patterns, if let and while let accept non-exhaustive patterns. You'll often see them used with Option:
fn main() {
if let Some(x) = std::env::args().len().checked_add(1) {
println!("{}", x);
} else {
println!("too many args :(");
}
}

Generalizing a function for an enum

I have an enum that looks like this
pub enum IpNetwork {
V4(Ipv4Network),
V6(Ipv6Network),
}
Each of those variants represents either a IPv4 or v6 CIDR. Now, Ipv4Network and Ipv6Network each has a method to get the prefix defined like this
// For Ipv4Network
pub fn prefix(&self) -> u8
// For Ipv6Network
pub fn prefix(&self) -> u128
How do I generalize the prefix method for the IpNetwork enum? I know that I can just have u128 as the return type, but is that approach idiomatic?
So you want a prefix function that operates on the IpNetwork type, but are unsure what the return type should be. Below is a possible approach you could follow.
The argument against using an enum
As bheklilr mentioned in a comment, one of the alternatives is introducing an enum: pub enum Prefix { V4(u8), V6(u128) }.
This could make sense depending on your use case, but it seems like overkill to me here. In the end, you would end up pattern matching on the result of your generic prefix function. In that case, you could better pattern match on the IpNetwork object itself and call its corresponding prefix function.
The case for u128
If you just want to obtain the integer value and don't need to differentiate between IPV4 and IPV6, returning an integer seems to be the way to go. A u8 can be casted to u128 without any problem and the overhead is negligible.
As far as I know the standard library doesn't hold functionality for generic numeric types. You could, however, define a trait and implement it for u8 and u128.
Also, there is the num crate, which does basically that.

Avoiding duplicate code when performing operation on different object properties

I have recently run into a problem which has had me thinking in circles. Assume that I have an object of type O with properties O.A and O.B. Also assume that I have a collection of instances of type O, where O.A and O.B are defined for each instance.
Now assume that I need to perform some operation (like sorting) on a collection of O instances using either O.A or O.B, but not both at any given time. My original solution is as follows.
Example -- just for demonstration, not production code:
public class O {
int A;
int B;
}
public static class Utils {
public static void SortByA (O[] collection) {
// Sort the objects in the collection using O.A as the key. Note: this is custom sorting logic, so it is not simply a one-line call to a built-in sort method.
}
public static void SortByB (O[] collection) {
// Sort the objects in the collection using O.B as the key. Same logic as above.
}
}
What I would love to do is this...
public static void SortAgnostic (O[] collection, FieldRepresentation x /* some non-bool, non-int variable representing whether to chose O.A or O.B as the sorting key */) {
// Sort by whatever "x" represents...
}
... but creating a new, highly-specific type that I will have to maintain just to avoid duplicating a few lines of code seems unnecessary to me. Perhaps I am incorrect on that (and I am sure someone will correct me if that statement is wrong :D), but that is my current thought nonetheless.
Question: What is the best way to implement this method? The logic that I have to implement is difficult to break down into smaller methods, as it is already fairly optimized. At the root of the issue is the fact that I need to perform the same operation using different properties of an object. I would like to stay away from using codes/flags/etc. in the method signature if possible so that the solution can be as robust as possible.
Note: When answering this question, please approach it from an algorithmic point of view. I am aware that some language-specific features may be suitable alternatives, but I have encountered this problem before and would like to understand it from a relatively language-agnostic viewpoint. Also, please do not constrain responses to sorting solutions only, as I have only chosen it as an example. The real question is how to avoid code duplication when performing an identical operation on two different properties of an object.
"The real question is how to avoid code duplication when performing an identical operation on two different properties of an object."
This is a very good question as this situation arises all the time. I think, one of the best ways to deal with this situation is to use the following pattern.
public class O {
int A;
int B;
}
public doOperationX1() {
doOperationX(something to indicate which property to use);
}
public doOperationX2() {
doOperationX(something to indicate which property to use);
}
private doOperationX(input ) {
// actual work is done here
}
In this pattern, the actual implementation is performed in a private method, which is called by public methods, with some extra information. For example, in this case, it can be
doOperationX(A), or doOperationX(B), or something like that.
My Reasoning: In my opinion this pattern is optimal as it achieves two main requirements:
It keeps the public interface descriptive and clear, as it keeps operations separate, and avoids flags etc that you also mentioned in your post. This is good for the client.
From the implementation perspective, it prevents duplication, as it is in one place. This is good for the development.
A simple way to approach this I think is to internalize the behavior of choosing the sort field to the class O itself. This way the solution can be language-agnostic.
The implementation in Java could be using an Abstract class for O, where the purpose of the abstract method getSortField() would be to return the field to sort by. All that the invocation logic would need to do is to implement the abstract method to return the desired field.
O o = new O() {
public int getSortField() {
return A;
}
};
The problem might be reduced to obtaining the value of the specified field from the given object so it can be use for sorting purposes, or,
TField getValue(TEntity entity, string fieldName)
{
// Return value of field "A" from entity,
// implementation depends on language of choice, possibly with
// some sort of reflection support
}
This method can be used to substitute comparisons within the sorting algorithm,
if (getValue(o[i], "A")) > getValue(o[j], "A"))
{
swap(i, j);
}
The field name can then be parametrized, as,
public static void SortAgnostic (O[] collection, string fieldName)
{
if (getValue(collection[i], fieldName)) > getValue(collection[j], fieldName))
{
swap(i, j);
}
...
}
which you can use like SortAgnostic(collection, "A").
Some languages allow you to express the field in a more elegant way,
public static void SortAgnostic (O[] collection, Expression fieldExpression)
{
if (getValue(collection[i], fieldExpression)) >
getValue(collection[j], fieldExpression))
{
swap(i, j);
}
...
}
which you can use like SortAgnostic(collection, entity => entity.A).
And yet another option can be passing a pointer to a function which will return the value of the field needed,
public static void SortAgnostic (O[] collection, Function getValue)
{
if (getValue(collection[i])) > getValue(collection[j]))
{
swap(i, j);
}
...
}
which given a function,
TField getValueOfA(TEntity entity)
{
return entity.A;
}
and passing it like SortAgnostic(collection, getValueOfA).
"... but creating a new, highly-specific type that I will have to maintain just to avoid duplicating a few lines of code seems unnecessary to me"
That is why you should use available tools like frameworks or other typo of code libraries that provide you requested solution.
When some mechanism is common that mean it can be moved to higher level of abstraction. When you can not find proper solution try to create own one. Think about the result of operation as not part of class functionality. The sorting is only a feature, that why it should not be part of your class from the beginning. Try to keep class as simple as possible.
Do not worry premature about the sense of having something small just because it is small. Focus on the final usage of it. If you use very often one type of sorting just create a definition of it to reuse it. You do not have to necessary create a utill class and then call it. Sometimes the base functionality enclosed in utill class is fair enough.
I assume that you use Java:
In your case the wheal was already implemented in person of Collection#sort(List, Comparator).
To full fill it you could create a Enum type that implement Comparator interface with predefined sorting types.

Resources