При определении обобщенных функций после названия функции в угловых скобках указываются параметры типа:
fn название_функции<T>(){
//.......
}
Затем через параметр типа мы можем определить тип параметров или тип возвращаемого результата. Например:
fn main(){
let result1 = receive(3);
println!("{}", result1); // 3
let result2 = receive("hello");
println!("{}", result2); // hello
}
fn receive<T>(item: T) -> T{
item
}
В данном случае функция receive() типизирована параметром типа T. Она имеет параметр этого типа - параметр item
и возвращает результат этого типа.
А при вызове этой функции в нее можно передавать значения различных типов.
Обобщенные методы определяются в виде:
impl<T> структура<T>{
fn название_метода(&self){
// выполняемые действия, которые используют параметр T
}
}
Параметр T после ключевого слова impl указывает, что метод применяется к обобщенному типу структура<T>.
Например:
struct Person<T>{
id: T,
name: String
}
impl<T> Person<T>{
fn get_id(&self) -> &T{
&self.id
}
}
fn main(){
let tom = Person{id:1, name: String::from("Tom")};
let tom_id = tom.get_id();
println!("{}", tom_id); // 1
let bob = Person{id:String::from("subadmin3"), name: String::from("Bob")};
let bob_id = bob.get_id();
println!("{}", bob_id); // subadmin3
}
В данном случае структура Person является обобщенной: ее поле id имеет тип, передаваемый через параметр T.
Объявление
impl<T> Person<T>{
указывает, что реализация методов относится именно к обобщенному типу Person<T>.
Далее в методе get_id() можно использовать этот параметр T, например, для определения типа возвращаемого результата.
Кроме определения обобщенных методов можно определить методы для структур и перечислений, которые ограничены конкретным типом. Например:
struct Person<T>{
id: T,
name: String
}
impl Person<u32>{
fn compare_id(&self, user_id: u32) -> bool{
self.id == user_id
}
}
fn main(){
let tom = Person{id:1, name: String::from("Tom")};
let result1 = tom.compare_id(1);
println!("result1: {}", result1); // result1: true
let bob = Person{id:4, name: String::from("Bob")};
let result2 = bob.compare_id(1);
println!("result2: {}", result2); // result2: false
}
Как и в предыдущем примере, структура Person по-прежнему обобщенная, где поле id представляет тип T. Но изменился тип, для которого определяется метод:
impl Person<u32>{
В данном случае мы говорим, что методы будут определяться только для типа Person<u32>, то есть фактически для объектов структуры Person, где параметр T будет представлять тип u32,
то есть где поле id будет представлять число типа u32.
В частности, здесь определяется метод compare_id(), который принимает число и сравнивает его со значением id текущего объекта (равен или нет).
Результат сравнения возвращается в качестве результата метода.
fn compare_id(&self, user_id: u32) -> bool{
self.id == user_id
}
И поскольку метод определяется для типа Person<u32>, мы знаем, что поле id будет представлять тип u32 и мы сможем проводить все те операции, которые доступны для этого типа. Например, как в данном случае операцию сравнения.
Причем поскольку метод определяется для типа Person<u32>, мы можем вызвать у объекта структуры, в котором id представляет целое число:
let tom = Person{id:1, name: String::from("Tom")};
let result1 = tom.compare_id(1); // все норм - id представляет целое число
Однако мы не сможем вызвать метод для структур Person, где параметр T представляет не тип u32, а какой-то другой тип, значение которого не преоразуется к типу u32. Например, в следующем случае мы получим ошибку:
let sam = Person{id: String::from("mas3"), name: String::from("Bob")};
let result3 = sam.compare_id(1); // ! Ошибка
Здесь поле id объекта sam имеет тип String, соответственно мы имеем дело со структурой типа Person<String>,
для которой НЕ существует метода compare_id, поэтому мы не можем вызвать этот метод у объекта sam.
Стоит отметить, что при определении методов эти методы могут иметь свои параметры типа, отличные от параметров типа структуры. Например:
struct Person<T>{
id: T,
name: String
}
impl<T> Person<T>{
fn clone_with_name<V>(&self, person_id: V) -> Person<V>{
Person{ id: person_id, name: self.name.clone()}
}
}
fn main(){
let tom = Person{id:1, name: String::from("Tom")};
let tom2 = tom.clone_with_name("235qwerty");
println!("Tom2 Info. Id: {} Name: {}", tom2.id, tom2.name); // Tom2 Info. Id: 235qwerty Name: Tom
}
В данном случае реализован метод clone_with_name, который создает новую структуру Person, копируя в нее из текущей структуры имя и устанавливая для нее новый id.
Структура Person типизирована типом T, который указывает на тип поля id текущей структуры. Однако метод clone_with_name сам типизирован параметром V, который позволяет установить
для создаваемой структуры Person поле id типа V.