Опционални аргументи с `Into<Option<_>>`

Рядко се случва да подаваме Option на функция. В повечето случаи, ако имаме стойност, викаме функция върху нея, иначе просто не викаме. Но понякога, една функция може да има много параметри, някои от които са опционални:

fn display_name(title: &str, first_name: &str, last_name: &str) -> String {
    format!("{} {} {}", title, first_name, last_name)
}

fn main() {
    println!("Hello, {}", display_name("Dr", "Jane", "Goodall"));
    println!("Hello, {}", display_name("Mr", "", "Anderson"));
    println!("Hello, {}", display_name("", "Jim", ""));
}

Изхода не е точно каквото бихме очаквали:

Hello, Dr Jane Goodall
Hello, Mr  Anderson
Hello,  Jim 

Интервалите остават от това, че "" не е "липса на име", а "празно име". Би имало смисъл да дефинираме функцията така:

fn display_name(title: Option<&str>, first_name: Option<&str>, last_name: Option<&str>) -> String {
   // ...
}

Проблема на това решение е, че трябва да викаме функцията с неща като display_name(Some("Dr"), Some("Jane"), Some("Goodall")). Опаковането на стойностите в Some не е от най-удобните интерфейси.

Вместо това, кода може да изглежда така:

fn display_name<'a, 'b, 'c>(
    title:      impl Into<Option<&'a str>>,
    first_name: impl Into<Option<&'b str>>,
    last_name:  impl Into<Option<&'c str>>,
) -> String {
    let parts = [title.into(), first_name.into(), last_name.into()];

    parts.
        into_iter().
        flat_map(|s| *s).
        collect::<Vec<_>>().
        join(" ")
}

fn main() {
    println!("Hello, {}", display_name("Dr", "Jane", "Goodall"));
    println!("Hello, {}", display_name("Mr", None, "Anderson"));
    println!("Hello, {}", display_name(None, "Jim", None));
}

Типа impl Into<Option<_>> е "някъв статичен тип, който имплементира trait-а Into<Option<_>>", така че функцията става generic по параметрите си и може да бъде извиквана с всякаква комбинация от option-и и низове (as it turns out, има тривиална имплементация за конвертиране на каквото и да е до Option: https://doc.rust-lang.org/src/core/option.rs.html#1060-1062)

Извикването на функцията е доста по-чисто, а и изхода е добър:

Hello, Dr Jane Goodall
Hello, Mr Anderson
Hello, Jim

Цената за това е по-сложна декларация на функцията, но ако пишете библиотека, това може да е напълно смислен tradeoff.