Помните, я писал, что в Rust нет перегрузки (overload) функций, ну т. е. мы не можем создать несколько функций с одним и тем же именем, но с разными типами для параметров и возвращаемого значения, что могло быть удобно. Но это и не нужно, когда мы можем сделать тоже самое с помощью генерик функции, что делает возможным ее использование с любыми типами - именно то, что мы и хотели при перегрузке функции.
Объявить “общий” (generic) тип можно с помощью “<” и “>” , в которые мы заключаем любое имя для одного или нескольких (через запятую) общих типов, например <T> или <K, V>. После чего мы можем использовать эти имена внутри функции, в качестве заглушек, на месте которых может быть любой базовый или пользовательский тип.
fn foo<T>(x: T) -> T { x }
Чтобы вызвать такую функцию, мы можем явно указать с помощью “::<i32>”, какой конкретный тип мы хотим использовать вместо общего типа. foo::<i32>(5); Или не указывать, и тогда Rust определит (inferr) его неявно, по типу значений для параметров и возвращаемого значения. foo(5); В данном случае значение “5” имеет тип “i32”.
Мы можем задать ограничение (trait bound) для общего типа, которое будет требовать, чтобы все конкретные типы имплементировали характеристику, т.е. гарантировали в своем составе определенные методы. Это делается с помощью “:Trait” или “:Trait1 + Trait2”, если характеристик несколько.
fn foo<T: Copy>(x: T) -> T { x }
Такое ограничение можно задать другим способом, с помощью “where T:Trait” или “where T: Trait1 + Trait2”. fn foo<T >(x: T) -> T where T: Copy + Clone { x } В данном случае конкретные типы должны поддерживать копирование.
Есть еще один способ задать ограничение, с помощью “impl Trait” вместо параметров и/или возвращаемого значения, когда нам не нужны имена-заглушки, но нужно гарантировать наличие характерных методов. Fn foo(x: impl Display)-> impl Display{ x } В данном случае тип будет выведен неявно на базе типа значения для параметра или возвращаемого значения с соблюдением условия, что этот тип имеет характеристику Display.
Дальше рассмотрим генерики в контексте пользовательских типов. Общий тип может быть использован для полей структуры и задается с помощью <T> после имени структуры. При создании такой структуры мы можем указать конкретный тип явно “::<i32>" или позволить Rustу вывести его.
Чтобы для такой структуры определить методы, которые могут использовать общий тип, нужно определить блок имплементации с помощью impl<T> , тем самым определяя имя заглушку для этого пространства имен (блока). Таким образом мы связываем заглушку структуры с заглушкой для методов.
Определение ограничений по характеристикам для методов такое-же, как и для обычных функций. Тут стоит отметить момент, при котором при определении ограничения для генерик-структуры, может потребовать соблюдение этого же ограничения для конкретного типа, который может быть использован для создания этой структуры. Например, мы можем сравнивать (PartialEq) такие структуры друг с другом при условии, что конкретные типы, использованные в этих структурах, также могут быть сравнимы. Смотрим пример.
point.rs
---------
use std::cmp::Ordering;
#[derive(Debug)]
pub struct Point<T> {
pub x: T,
pub y: T,
}
impl Point<i32>{
//..
}
impl<T> Point<T> {
pub fn from(_x: T, _y: T) -> Point<T> {
Point {
x: _x,
y: _y,
}
}
pub fn set(&mut self, _x: T, _y: T) {
self.x = _x;
self.y = _y;
}
pub fn bigger(p1: Point<T>, p2: Point<T>) -> Option<Point<T>> where T: PartialOrd + PartialEq {
if p1 == p2 {
None
} else if p1 < p2 {
Some(p2)
} else {
Some(p1)
}
}
}
impl<T> PartialEq for Point<T> where T: PartialEq {
fn eq(&self, other: &Self) -> bool {
if self.x == other.x && self.y == other.y {
true
} else {
false
}
}
}
impl<T> PartialOrd for Point<T> where T: PartialOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.x.partial_cmp(&other.x)
}
}
main.rs
-----------
mod point;
use point::*;
fn main() {
let p1 = Point { x: 5.0, y: 5.0 };
//let p1 = Point::<f64> { x: 5.0, y: 5.0 };
//let p1:Point<f64> = Point { x: 5.0, y: 5.0 };
let mut p2 = Point::<f64>::from(0.1, 0.1);
p2.set(10.1, 10.1);
//println!("Bigger point is: {:?}", Point::<f64>::bigger(p1, p2).unwrap());
println!("Bigger point is: {:?}", Point::bigger(p1, p2).unwrap());
}
Перегуд В.
Комментариев нет:
Отправить комментарий