Idea: invert() or toggle() function for boolean type?

This is a fairly small issue I have with booleans - often I want to toggle some boolean (if it’s true, set to false, if it’s false, set to true), but it happens that the boolean is fairly nested. So I have to type something like:

a_very_long_struct.my_field.my_nested_field.data.state[idx].blah = !a_very_long_struct.my_field.my_nested_field.data.state[idx].blah;

What I’d like to have is some kind of .invert() or .toggle() function on the boolean type itself, so I don’t have to write the whole path a second time:

a_very_long_struct.my_field.my_nested_field.data.state[idx].blah.invert();

Of course I can write this function myself or use a &mut bool in front, but neither are very clean solutions:

{
    let x = &mut a_very_long_struct.my_field.my_nested_field.data.state[idx].blah;
    *x != *x;
} // borrow checker - need to release &mut on a_very_long_struct

or with an external crate:

fn toggle(data: &mut bool) {
    *data != *data;
}

use my_crate::toggle;
toggle(&mut a_very_long_struct.my_field.my_nested_field.data.state[idx].blah);

While I can create a function like that myself, it’s a bit annoying not to have it in the standard library or implemented directly on the bool type, so I wanted to ask if it would be a good idea to implement this.

9 Likes

You can also do

some_bool ^= true;

although perhaps it’s less readable.

12 Likes

Thankfully NLL will remove the need for these braces in Rust 2018 Edition.

You can make this a method by writing an extension trait:

trait BoolToggleExt {
    fn toggle(&mut self);
}

impl BoolToggleExt for bool {
    fn toggle(&mut self) {
        *self = !*self;
    }
}

use my_crate::BoolToggleExt;
a_very_long_struct.my_field.my_nested_field.data.state[idx].blah.toggle();
6 Likes

I know that I can make my own function, but then I have to import the trait again and I can’t reuse it inbetween projects without importing an external library. Which is why I was asking if anyone has objections to putting it in the standard library. I mean it doesn’t add much to the language complexity.

And no, NLL won’t help with the braces, since NLL can’t reason about array access. It will still think that data.state is borrowed. My point was, having to make an intermediate variable does not look nice.

2 Likes

I don’t remember the last time I wanted a &mut bool -> () operation but if this is common enough then it seems worthwhile as a standard library addition for the improved readability; .toggle() seems the most “boolean” sounding operation; .flip() might be another word for it.

3 Likes

I’ve wanted this a few times, but ended up using Option<()> and take(), so maybe fn clear(&mut self) -> bool and fn set(&mut self) -> bool would be more useful?

I'm not sure if this is your intent, but your use of emphasis here comes across as extremely hostile! I know that you know this. I read the post from top to bottom and back. I was responding to these specific words: (emphasis mine)

to point out that method syntax can be recovered.


NLL allows overlapping mutable borrows to coexist as long as old ones are never reused.


For me, the default answer to "should it be in the standard library" on anything I haven't previously thought about is always "no." That is why my first reply focused on specific details in your post to see if any of them changes the landscape of the problem enough for another solution to be workable. And were this not the case, my next response probably would have been something along the lines of:

Well, I suppose there aren't an awful lot of other things that one might need to do with a bool, so adding a method for this seems harmless. (Modulo the color of the bike shed.)

2 Likes

or you can use the explicit form

some_bool.bitxor_assign(true);

That seems less readable than

some_bool.toggle();

the intent signaled is quite different. With the former I have to stop and think "ah, this is just flipping the truthiness" and with the latter there's no thinking.

4 Likes

For all integer primitives and for bool, currently bitand, bitor and bitxor have counterparts bitand_assign, bitor_assign and bitxor_assign but not does not have a counterpart. This is probably because there are &=, |= and ^= operators but no operator for bitwise inversion of self.

An invert or toggle function would be a counterpart to not, though there is no operator. I think invert is better than toggle as it can be applied to integers as well as bool, where it can be useful too though not as much as for bool.

1 Like

I think you mean *x = !*x;

And *data = !*data;

I find ^= true more readable than .toggle(), personally. I've been using that for years in multiple different languages, and don't see the need to invent a name for it that ends up being longer anyway. (Particularly since there's no usable chaining that can be involved here.)

1 Like

That’s fair; I don’t do much bitwise operations so when I see ^= true I still have to wait and think about the true here. I suspect that you do these kind of operations more than me so you are more used to it?

1 Like

I'm not sure about toggling exactly but there are a few similar "convenience methods" I've wanted in the past (and some random names):

  • reset -- equivalent to mem::replace(path, false). I often want to do something like while dirty.reset() {... dirty = true; ...}
  • increment -- equivalent to fetch_add(1, Ordering::SeqCst) but for ordinary integers, not atomic ones

Similar to the poster here, I've never quite felt sufficiently motivated to make a helper library for these.

2 Likes

For that matter, I would like to have a method not that is equivalent to !x, just because prefix operators are annoying. =)

Not in std::ops - Rust :stuck_out_tongue:

Long live the post-fix operators!

2 Likes

What other languages given you ^ as a first-class thing for bools? I've never seen this before (beyond using != for bools as a hacky version thereof).

1 Like

Almost every language I know (that I picked out on TIO in a quick pass through) offered boolean xor via ^:

9 Likes

My entire life has been a lie.

8 Likes

x.add_assign(1) (or x += 1) seem like reasonable approximations in this case.

If we had an increment method then people would want a ++ operator to go with it. :slight_smile: