Return value if passes predicate, else default

2019-07-24 21:35发布

How can I replace a value if it fails a predicate?

To illustrate:

assert_eq!((3-5).but_if(|v| v < 0).then(0), 0)

I thought there would be something on Option or Result to allow this, but I cannot find it.

2条回答
放荡不羁爱自由
2楼-- · 2019-07-24 22:25

I thought there would be something on Option or Result

But neither of these types appear here. Subtracting two numbers yields another number.

It appears you just want a traditional if-else statement:

fn main() {
    let a = 3 - 5;
    assert_eq!(if a < 0 { 0 } else { a }, 0);
}

Since you have two values that can be compared, you may also be interested in max:

use std::cmp::max;

fn main() {
    assert_eq!(max(0, 3 - 5), 0);
}

You can make your proposed syntax work, but I'm not sure it's worth it. Presented without further comment...

fn main() {
    assert_eq!((3 - 5).but_if(|&v| v < 0).then(0), 0)
}

trait ButIf: Sized {
    fn but_if<F>(self, f: F) -> ButIfTail<Self>
        where F: FnOnce(&Self) -> bool;
}

// or `impl<T> ButIf for T {` for maximum flexibility
impl ButIf for i32 {
    fn but_if<F>(self, f: F) -> ButIfTail<Self>
        where F: FnOnce(&Self) -> bool,
    {
        ButIfTail(f(&self), self)
    }
}

struct ButIfTail<T>(bool, T);

impl<T> ButIfTail<T> {
    fn then(self, alt: T) -> T {
        if self.0 {
            alt
        } else {
            self.1
        }
    }
}
查看更多
爷、活的狠高调
3楼-- · 2019-07-24 22:33

Update: This got a bit nicer since Rust 1.27, when Option::filter was added:

assert_eq!(Some(3 - 5).filter(|&v| v >= 0).unwrap_or(0), 0);

Prior to Rust 1.27, you would have needed an iterator in order to write a single, chained expression without lots of additional custom machinery:

assert_eq!(Some(3 - 5).into_iter().filter(|&v| v >= 0).next().unwrap_or(0), 0);
查看更多
登录 后发表回答