Skip to content

Instantly share code, notes, and snippets.

@ayazhafiz
Last active October 19, 2020 01:56
Show Gist options
  • Save ayazhafiz/a3d974caa0c762cc68005a28920a835a to your computer and use it in GitHub Desktop.
Save ayazhafiz/a3d974caa0c762cc68005a28920a835a to your computer and use it in GitHub Desktop.
rust :):

Things i wish i could do in rust.

Enum variants as types

What I want is

enum Animal { Rabbit, Lizard }
fn do_rabbit(r: Animal::Rabbit) {}
fn do_lizard(l: Animal::Lizard) {}

Currently if you want to do such a thing, you have to use the following pattern

struct Rabbit;
struct Lizard;
enum Animal { Rabbit(Rabbit), Lizard(Lizard) }
fn do_rabbit(r: Rabbit) {}
fn do_lizard(l: Lizard) {}

In many cases where the latter pattern is needed, creating an encapsulating enum (like Animal) is just extra noise that is better alleviated by an animal namespace (mod) or a trait.

It would be nice if variants could be referred to as types directly, but the project has previously mentioned that this kind of thing is "unbounded work", and my guy instinct is that implementation of this would be orthogonal to much of what is in rustc.

Struct composition

Sometimes i need to do something like

struct Input {
  /* a bunch of fields */
}

struct SanitizedInput {
  input: Input,
  sanitized_brand: std::marker::PhantomData<SanitizedBrand>,
}

Which is... fine, because i can get around having to have an extra property access for each instance of SanitizedInput if i make Input's fields visible on a trait and implement that for SanitizedInput, but obviously this is a lot of work for something i don't want to be doing. Ideally SanitizedInput could just be an opaque header over an Input.

So it would be nice if i could define SanitizedInput as a composite type:

struct SanitizedInput: Input {
  sanitized_brand: std::marker::PhantomData<SanitizedBrand>,
}

Partial enum decomposition

Sometimes I want to guard on an enum variant, and handle all other variants in a separate branch with knowledge that only those variants are applicable. For example consider the code

let conf = send(big_ol_response);
if let Some(Status::Ok) = conf {
  return;
}

let response = recompute_super_expensive_response();
/* do some other stuff */
match conf {
  Status::RateLimited => send_with_rl(response),
  Status::Redirect => send_with_proxy(response),
  Status::Ok => unreachable!(),
}

(okay yes i know this is contrived and doesn't make sense.. but it's ok)

That unreachable branch is... suboptimal. And yes, you could factor everything out into a separate method and then have a match block where every variant takes at most one line. But vertical<->horizontal shifting is unpleasant, and I would like to keep vertical as much as possible.

Furthermore, a typechecker can definitely tell that Status::Ok is never possible inside the match block of the example above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment