Создание функции Rust, которая принимает String или &str
Перевод | Автор оригинала: Herman J. Radtke III
В моем последнем посте мы много говорили об использовании &str в качестве предпочтительного типа для функций, принимающих строковый аргумент. Ближе к концу этого поста было обсуждение того, когда использовать String vs &str в структуре. Я думаю, что это хороший совет, но бывают случаи, когда использование &str вместо String не оптимально. Нам нужна другая стратегия для этих случаев использования.
Структура, содержащая строки
Рассмотрим структуру Person ниже. Для обсуждения предположим, что человеку действительно необходимо владеть переменной name. Мы решили использовать тип String вместо &str.
struct Person {
name: String,
}
Теперь нам нужно реализовать функцию new(). Основываясь на моем последнем сообщении в блоге, мы предпочитаем &str:
impl Person {
fn new (name: &str) -> Person {
Person { name: name.to_string() }
}
}
Это работает до тех пор, пока мы не забываем вызывать .to_string() внутри функции new(). Однако эргономика этой функции ниже, чем хотелось бы. Если мы используем строковый литерал, мы можем создать новый объект Person, например Person.new («Герман»). Если у нас уже есть String, нам нужно запросить ссылку на String:
let name = "Herman".to_string();
let person = Person::new(name.as_ref());
Хотя такое чувство, что мы ходим по кругу. У нас была String, затем мы вызвали as_ref(), чтобы превратить ее в &str, а затем снова превратить в String внутри функции new(). Мы могли бы вернуться к использованию String, например fn new (name: String) -> Person {, но это означает, что нам нужно заставить вызывающего абонента использовать .to_string() всякий раз, когда есть строковый литерал.
В конверсии
Мы можем упростить работу вызывающей программы с помощью свойства Into. Эта трэйта может автоматически преобразовывать &str в строку. Если у нас уже есть String, преобразование не происходит.
struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
Этот синтаксис для new() выглядит немного иначе. Мы используем Generics и Traits, чтобы сообщить Rust, что некоторый тип S должен реализовывать трейт Into для типа String. Тип String реализует Into
Не беспокойтесь, если вас сбивают с толку такие вещи, как статическая диспетчеризация и мономорфизация. Вам просто нужно знать, что, используя приведенный выше синтаксис, вы можете создавать функции, которые принимают как String, так и &str. Если вы думаете, что fn new <S: Into
Синтаксис where также работает, и его может быть легче читать, особенно если подпись функции становится более сложной:
struct Person {
name: String,
}
impl Person {
fn new<S>(name: S) -> Person where S: Into<String> {
Person { name: name.into() }
}
}
Связанный
- String vs &str в функциях Rust
- Создание функции Rust, которая возвращает &str или String