Home > Back-end >  Can I return immediately when meets None while function chaining?
Can I return immediately when meets None while function chaining?

Time:09-24

Problem.
I'm curious about this because if it can, I don't need if thing in this code I think.
I want that especially .filter_map(|ch| ch.to_digit(10)).
ch.to_digit() returns None if ch is not digit. then I want return false.
If I can do this, I think I don't need if !code.chars().all(|ch| ch.is_digit(10) || ch.is_whitespace()) this check. (I just wanted to know can I do that if checks within chaining.)

If it's not allowed, is there some way to do exception check in function chain?
Thanks.

/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {
    if code.trim().len() <= 1 {
        return false;
    }
    if !code.chars().all(|ch| ch.is_digit(10) || ch.is_whitespace()) {
        return false;
    }

    code.chars()
        .rev()
        .filter_map(|ch| ch.to_digit(10))
        .enumerate()
        .map(|( i, n)|
            match i % 2 {
                0 => n,
                _ if n == 9 => n,
                _ => (n * 2) % 9,
            }
        )
        .sum::<u32>() % 10 == 0
}

CodePudding user response:

You can with try_fold:

pub fn is_valid(code: &str) -> bool {
    code.chars()
        .rev()
        .try_fold((0, 0), |acc, ch|
            // `acc.0` enumerates the valid digits
            // `acc.1` accumulates the sum
            if ch.is_whitespace() {
                Some (acc)
            } else {
                ch.to_digit (10).map (|n|
                    match acc.0 % 2 {
                        0 => (acc.0 1, acc.1   n),
                        _ if n == 9 => (acc.0 1, acc.1   n),
                        _ => (acc.0 1, acc.1   (n*2)%9),
                    })
            })
        .map (|acc| acc.1 % 10 == 0)
        .unwrap_or (false)
}

Playground

However for readability's sake, I would go with @Masklinn's straightforward for loop.

CodePudding user response:

Can I return immediately when meets None while function chaining?

Not really. You could map to an Option (instead of filter_map) then collect the Iterator<Option<_>> to an Option<Vec<_>>:

let items = vec![0_u16, 1, 2];

let res: Option<Vec<u16>> = items
    .iter()
    .map(|x| x.checked_add(1))
    .collect();

assert_eq!(res, Some(vec![1, 2, 3]));

let res: Option<Vec<u16>> = items
    .iter()
    .map(|x| x.checked_sub(1))
    .collect();

assert_eq!(res, None);

then you'd bail if you have a None at the end.

But in all honestly I'd just use a bog-standard for loop e.g.:

let mut sum = 0;
for (i, n) = code.chars().rev().map(|ch| ch.to_digit(10)).enumerate() {
    sum  = match (i % 2, n) {
        (_, None) => return false;
        (0, Some(n)) => n,
        (_, Some(9)) => 9,
        (_, Some(n)) => (n * 2) % 9,
    }
}
sum % 10 == 0

If it's not allowed, is there some way to do exception check in function chain?

Not sure what you mean.

  • Related