0
点赞
收藏
分享

微信扫一扫

AI图书推荐:用ChatGPT快速创建在线课程

程序员伟杰 2024-05-07 阅读 13

大家好,

欢迎来到学习 Rust 的第16天。今天,我们来看看 Rust 中的泛型类型,泛型类型帮助我们以类型安全的方式减少代码重复。

让我们快速了解一下。

引言

Rust 中的泛型类型允许你编写可以操作多种数据类型的函数、结构体和枚举,而不牺牲类型安全,从而实现代码复用和灵活性。来看一个用例。

fn find_max(x: i32, y: i32) -> i32{  
  if x > y {  
    x  
  }else{  
    y  
  }  
}  
  
fn main(){  
  let result: i32 = find_max(9,10);  
  println!("最大值是:{}", result);  
}

输出:

最大值是:10

如果我想使用相同的函数处理字符或浮点数,我必须为特定的类型创建一个新函数,逻辑基本相同。泛型类型有助于减少这种代码重复。

我们可以为函数/结构体声明多个泛型类型。单个泛型类型通常用 T 表示,代表 Type

函数中的泛型类型用 <> 声明。

示例:

fn find_max<T: PartialOrd>(x: T, y: T) -> T {  
    if x > y {  
        x  
    } else {  
        y  
    }  
}  
  
fn main() {  
    let result = find_max(10, 5);  
    println!("最大值是:{}", result);  
  
    let result_float = find_max(4.5, 3.2);  
    println!("最大值是:{}", result_float);  
  
    let result_char = find_max('x','a');  
    println!("最大值是:{}", result_char);  
}

输出:

最大值是:10  
最大值是:4.5  
最大值是:x

解释:

  • find_max 函数用泛型类型参数 T 定义。
  • <T: PartialOrd> 语法表明 T 必须实现 PartialOrd 特性,这允许对 T 类型的值进行比较。
  • PartialOrd 是 Rust 中定义值之间部分排序关系(小于、小于等于、大于、大于等于)的特性。
  • 实现 PartialOrd 的类型可以使用比较运算符(<<=>>=)进行比较,并可用于依赖排序的函数,比如排序算法或在此例中,寻找最大值。
  • 这使得该函数能够处理各种类型,只要它们支持使用 PartialOrd 特性的比较。
  • 通过使用泛型,函数在保持类型安全的同时获得了灵活性和可重用性。
  • 该函数返回一个 T 类型的值,确保最大值与输入值类型相同。

结构体中的泛型类型

假设我想存储一个点的坐标。这是我通常使用的结构体

struct Point{  
  x: i32,  
  y: i32,  
}  
  
fn main(){  
  let point = Point{x: 3, y: 10};  
  
  //这行代码不会工作,因为我们的结构体只能存储有符号的32位整数  
  let point2 = Point{x:3.4, y: 6.9};  
}

让我们解决这个问题:

struct Point<T>{  
  x: T,  
  y: T,  
}  
  
fn main(){  
  let point = Point{x: 3, y: 10};  
  let point2 = Point{x:3.4, y: 6.9};  
  
  //这行代码不会工作,因为我们的结构体不能存储两种不同的数据类型  
  let point3 = Point{x:3.4, y: 6};  
}

为了解决这个问题,我们可以在结构体中添加另一个泛型类型。

struct Point<T, U>{  
  x: T,  
  y: U,  
}  
  
fn main(){  
  let point = Point{x: 3, y: 10};  
  let point2 = Point{x:3.4, y: 6.9};  
  let point3 = Point{x:3.4, y: 6};  
}

现在这一切都能工作了,这就是我们如何在结构体中使用泛型类型…

枚举中的泛型类型

我们可以对枚举做同样的事情,我将给你展示两个最流行的带有泛型类型的枚举示例:

enum Option<T>{  
    Some(T),  
    None,  
}

enum Result<T,E>{  
  Ok(T),  
  Err(E),  
}

方法中的泛型类型

如果你还记得,我们可以使用 impl 为结构体声明实现块,以声明结构体的方法。

回到我们的 Point 结构体,让我们使用泛型类型为结构体声明一个实现块。

struct Point<T>{  
  x: T,  
  y: T,  
}  
  
impl<U> Point<U>{  
  fn x(&self) -> &U {  
    &self.x  
  }  
}  
  
fn main(){  
  let point = Point{x: 3, y: 10};  
  let point2 = Point{x:3.4, y: 6.9};  
}

X 方法返回点的 X 坐标,取自 self 的引用。

注意,声明结构体时使用了 T 作为泛型类型,在 impl 块中我使用了 U,这表明这两个泛型类型并不相连。

如果我声明另一个具体类型的 impl 块会发生什么?

struct Point<T>{  
  x: T,  
  y: T,  
}  
  
impl<U> Point<U>{  
  fn x(&self) -> &U {  
    &self.x  
  }  
}  
  
impl Point<i32>{  
  fn y(&self) -> i32{  
    self.y  
  }  
}  
  
fn main(){  
  let point1 = Point{x: 3, y: 10};  
  let point2 = Point{x:3.4, y: 6.9};  
  
  println!("X: {}, Y: {}",point1.x(),point1.y());  
  println!("X: {}, Y: {}",point2.x(),point2.y);  
}

y 方法只对实例可用,这些实例的 xy 值为有符号的 32 位整数,而 x 方法可以用于任何类型的实例。

混合使用泛型

让我们看一个更复杂的例子,我们的 Point 结构体中有两个泛型,并理解混合它们的需要…

struct Point<T, U>{  
  x: T,  
  y: U,  
}  
  
impl<T,U> Point<T,U>{  
  fn mixup<V, W>(self,other: Point<V, W>) -> Point<V, W>{  
    Point{  
      x: self.x,  
      y: other.y,  
    }  
  }  
}  
  
fn main(){  
  let point = Point{x: 3, y: 10};  
  let point2 = Point{x:3.4, y: 6.9};  
  let point3 = Point{x:3.4, y: 6};  
  let point4 = point1.mixup(point2);  
  
  println!("X: {}, Y: {}",point4.x, point4.y);  
}

为什么要混合使用泛型?

VW 像是类型的占位符。我们在 mixup 方法中使用它们,因为它允许我们混合匹配两个 Point 实例的 xy 值的不同类型。这种灵活性使我们的代码更加多样化,因为它允许我们处理不同的类型而无需改变结构体本身。所以,使用 VW 而不是 TU,在方法中保持事物清晰且灵活。

结论

总之,泛型类型是减少代码重复的绝佳方式,因此它们对性能有巨大的影响,通过在 Rust 中利用泛型类型,开发者可以编写更多样化和可重用的代码,同时保持类型安全和性能。这种方法不仅减少了冗余,还增强了代码的可维护性和可扩展性,使 Rust 成为广泛应用程序的强大语言选择。此外,拥抱泛型促进了更清晰、更灵活的设计,使开发者能够轻松适应不断变化的需求。

举报

相关推荐

0 条评论