RFC: trick to avoid funcname(bool, bool, bool)


#1

I have a function which needs a few boolean values. To avoid call sites looking like

x = blah (true, false, true);

where one would have no idea of what the arguments represent, I came up with the following:

#[derive(PartialEq)]
pub enum Fubarize {
  No,
  Yes
}

#[derive(PartialEq)]
pub enum Frobnify {
  No,
  Yes,
}

pub fn blah (fubarize: Fubarize, frobnify: Frobnify) -> i32 {
  if fubarize == Fubarize::Yes {
    ...
  }
}

This is a bit awkward in the implementation, but I really like the callers looking like

x = blah (Fubarize::Yes, Frobnify::No);

Alternative: newtypes on booleans, so you’d have

pub struct Fubarize(bool);
pub struct Frobnify(bool);

...
x = blah (Fubarize (true), Frobnify(false));

Opinions, or suggestions about different patterns, are appreciated!


#2

You can get around providing the #[derive(PartialEq)] by using if let:

pub fn blah (fubarize: Fubarize, frobnify: Frobnify) -> i32 {
  if let Fubarize::Yes = fubarize {
    ...
  }
}

which is equivalent to

pub fn blah (fubarize: Fubarize, frobnify: Frobnify) -> i32 {
  match fubarize {
      Fubarize::Yes => {
          ...
      }
      _ => (),
  }
}

#3

Personally, I think bitflags would be more ergonomic/concise and still quite readable. Multiple newtype wrappers over bool for this purpose seems excessive. Also, it’d be nice if Rust picks up named args at some point.


#4

Passing the named arguments with a struct works well.


#5

An IDE that gives you the details quick is best.

x = blah({let x=true;x}, {let y=false;y}, {let z=true;z});
or switch to const with added type.


#6

(responding to @jonh, the software doesn’t make it really clear when the post is directly following)

That’s so weird of a hack that you may as well do.

let x = true;
let y = false;
let z = true;
let x = blah(x, y, z);

#7

Although, considering this is a C style enum, why not just use #[derive(Eq, PartialEq)] :slight_smile:. It’s not that it may suddenly change to have fields that don’t implement Eq.


#8

@federicomenaquintero For what it’s worth I like your newtypes alternative the best. It looks like the simplest to implement and cleanest to read.


#9

You can also write:

struct Fubarize(bool);
struct Frobnify(bool);

fn blah(Fubarize(fub): Fubarize, Frobnify(frob): Frobnify) {
    println!("{} {}", fub, frob);
}

fn main() {
    blah(Fubarize(true), Frobnify(false));
}

But this is more verbose and less DRY than nice named arguments.


#10

TIL that function arguments are also pattern matches. Thanks!


#11

Yes, but they can only be used with so-called irrefutable patterns, so a tuple or a struct works, but not an enum, just like in let.