При определении обобщенных функций после названия функции в угловых скобках указываются параметры типа:
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.