7

In C++, we can overload operator bool() to convert a struct to bool:

struct Example {
    explicit operator bool() const {
        return false;
    }
};

int main() {
    Example a;
    if (a) { /* some work  */ }
}

Can we do something simple (and elegant) in Rust so to:

pub struct Example {}

fn main() {
    let k = Example {};
    if k {
        // some work
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Rahn
  • 4,787
  • 4
  • 31
  • 57
  • 5
    Another fine example why C++ is such a "dangerous" language. Even if `Example` is not a boolean type, it is used as such. So the real conversion is hidden, and from the source it is **not** clear what is meant. :-( – the busybee Nov 24 '21 at 14:31
  • 3
    What is your use case for this? Is this really a type conversion, or do you want to assign a "truthiness" to a data type, e.g. `false` means empty container and `true` means non-empty? The idiomatic way to do this in Rust depends on your actual use case. – Sven Marnach Nov 24 '21 at 15:02
  • While that may be "simple", I'd argue that it's *not* "elegant". What does `if k` even mean? You're better off implementing a specific method that returns a boolean and calling that -- eg `if k.has_data()` – Herohtar Nov 24 '21 at 16:40

4 Answers4

11

There's no direct equivalent of operator bool(). A close alternative would be to implement From (which will also implement Into) and call the conversion explicitly:

pub struct Example;

impl From<Example> for bool {
    fn from(_other: Example) -> bool {
        false
    }
}

fn main() {
    let k = Example;
    if k.into() {
        // some work
    }
}

This will take ownership of Example, meaning you can't use k after it's been converted. You could implement it for a reference (impl From<&Example> for bool) but then the call site becomes uglier ((&k).into()).

I'd probably avoid using From / Into for this case. Instead, I'd create a predicate method on the type. This will be more readable and can take &self, allowing you to continue using the value.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    [The API guidelines](https://rust-lang.github.io/api-guidelines/interoperability.html#conversions-use-the-standard-traits-from-asref-asmut-c-conv-traits) state that `Into` should never be implemented. Since this is a rather highly voted answer, it should probably be implemented with the idiomatic `From for bool`? – sebpuetz Nov 24 '21 at 14:48
  • @sebpuetz a good point. It's _less_ needed since a few Rust versions back, but we still use `From` for consistency. – Shepmaster Nov 24 '21 at 14:49
  • 4
    It's not only for consistency. Implementing `From for U` gives you `Into for T` for free because of the blanket impl, while implementing `Into for T` directly doesn't give you a `From` impl. – Sven Marnach Nov 24 '21 at 14:57
  • @SvenMarnach my point is that relatively recent Rust versions removed the entire reason that both `From` and `Into` existed in the first place. If the standard library were designed today, there would only be `Into`. That wasn't true in Rust 1.0. – Shepmaster Nov 24 '21 at 15:27
4

Rust does not have C++'s implicit type conversion via operator overloading. The closest means of implicit conversion is through a Deref impl, which provides a reference of a different type.

What is possible, albeit not necessarily idiomatic, is to implement the not operator ! so that it returns a boolean value, and perform the not operation twice when needed.

use std::ops::Not;

pub struct Example;

impl Not for Example {
    type Output = bool;
    
    fn not(self) -> bool { false }
}

fn main() {
    let k = Example;
    if !!k {
        println!("OK!");
    } else {
        println!("WAT");
    }
}

Playground

E_net4
  • 27,810
  • 13
  • 101
  • 139
3

You have a few options, but I'd go for one of these:

Into<bool> (From<Example>)

If your trait conceptually represents a bool, but maybe with some extra metadata, you can implement From<Example> for bool:

impl From<Example> for bool {
    fn from(e: Example) {
        // perform the conversion
    }
}

Then you can:

fn main() {
    let x = Example { /* ... */ };
    if x.into() {
        // ...
    }
}

Custom method

If your type doesn't really represent a boolean value, I'd usually go for an explicit method:

impl Example {
    fn has_property() -> bool { /* ... */ }
}

This makes it more obvious what the intent is, for example, if you implemented From<User> for bool:

fn main() {
    let user = User { /* ... */  };

    if user.into() {
        // when does this code get run??
    }

    // compared to
    if user.logged_in() {
        // much clearer
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
cameron1024
  • 9,083
  • 2
  • 16
  • 36
2

You can implement std::ops::Deref with the bool type. If you do that, you have to call *k to get the boolean.

This is not recommended though, according to the Rust documentation:

On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.

struct Example {}

impl std::ops::Deref for Example {
    type Target = bool;

    fn deref(&self) -> &Self::Target {
        &true
    }
}

fn main() {
    let k = Example {};
    if *k {
        // some work
    }
}

Playground

tversteeg
  • 4,717
  • 10
  • 42
  • 77