Есть ли встроенный способ "объединить" два параметра?

В следующей примерной программе есть ли способ избежать определения map2?

fn map2<T, U, V, F: Fn(T, U) -> V>(f: F, a: Option<T>, b: Option<U>) -> Option<V> {
    match a {
        Some(x) => match b {
            Some(y) => Some(f(x, y)),
            None => None,
        },
        None => None,
    }
}

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |a, b| {
        a + b
    };
    let res = map2(f, a, b);
    println!("{:?}", res);
    // prints Some(15)
}

Для людей, которые также говорят на Haskell, я думаю, этот вопрос можно было бы также сформулировать как "Есть ли какой-нибудь инструмент, который мы можем использовать вместо liftM2 в Русте?"

Ответ 1

Я не верю, что существует прямая функция, эквивалентная liftM2, но вы можете комбинировать Option::and_then и Option::map:

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |a, b| {
        a + b
    };

    println!("{:?}", a.and_then(|a| b.map(|b| f(a, b))));
}

Вывод:

Some(15)

Ответ 2

Я не знаю, можно ли перейти к одной строке (Правка: о принятом ответе, он хорошо переносится на одну строку), но вы можете избежать вложенного match, сопоставив его с кортежем:

let a = Some(5);
let b = Some(10);
let f = |a, b| {
    a + b
};
let res = match (a, b) {
    (Some(a), Some(b)) => Some(f(a, b)),
    _ => None,
};
println!("{:?}", res);
// prints Some(15)

Ответ 3

Вы можете использовать тот факт, что Option можно повторить. Перейдите по обоим параметрам, запишите их вместе и сопоставьте полученный итератор с вашей функцией.

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |(a, b)| {
        a + b
    };
    let res = a.iter().zip(b.iter()).map(f).next();
    println!("{:?}", res);
    // prints Some(15)
}

Для этого потребовалась модификация f, поэтому аргументы объединяются в один кортеж. Было бы возможно без изменения f путем прямого сопоставления над |args| f.call(args), но тогда вам нужно указать тип закрытия f.

Ответ 4

let num_maybe = Some(5);
let num_maybe2 = Some(10);
let f = |a, b| {
    a + b
};

Опция 1

if let (Some(a), Some(b)) = (num_maybe, num_maybe2) {
    f(a, b)
}

Вариант 2

num_maybe.and_then(|a| num_maybe2.map(|b| f(a, b))

Вариант 3

[num_maybe, num_maybe2].into_iter().flatten().fold(0, f)