Опционални аргументи с `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.