Rust语法: 枚举,泛型,trait

这篇具有很好参考价值的文章主要介绍了Rust语法: 枚举,泛型,trait。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这是我学习Rust的笔记,本文适合于有一定高级语言基础的开发者看不适合刚入门编程的人,对于一些概念像枚举,泛型等,不会再做解释,只写在Rust中怎么用。

枚举

枚举的定义与赋值

枚举的定义格式如下:
enum 枚举名{
值1(附加类型),
值2(附加类型),…
}
其中,关联类型可以省去

例如要创建一个神经网络类型的枚举,就可以这样定义

enum NeuralNetwork {
    CNN,
    RNN,
    GAN,
    Transformer,
    GNN
}

下面是传参和创建的例子,其中引用的部分可以看后面的,所有权&生命周期这一部分。

enum NeuralNetwork {
    CNN,
    RNN,
    GAN,
    Transformer,
    GNN
}

fn main(){
    let nn1 = NeuralNetwork::CNN; //创建一个NeuralNetwork类型的枚举,值为CNN
    let nn2 = NeuralNetwork::Transformer;
    let nn3 = nn1;
    printnn(&nn3); //给函数传参
    printnn(&nn2);
}

fn printnn(network: &NeuralNetwork){} //实参表明了需要一个NeuralNetwork类型的引用()

除此之外,枚举可以增加附类型

enum NeuralNetwork { // 所有的网络类型都带有一个附加类型,String表示具体的网络名
    CNN(String),
    RNN(String),
    GAN(String),
    Transformer(String),
    GNN(String)
}
enum Test{ // 所有的网络类型都带有一个附加类型,String表示具体的网络名
    e1,
    e2(String), 
    e3{x: u32, y: f64}, //绑定了一个匿名结构体类型
    e4(u32, u32, u32) //绑定一个Tuple
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN")); 
    let nn2 = NeuralNetwork::Transformer(String::from("Transformer-XL"));

}

枚举绑定方法和函数

与结构体类型,对于枚举,Rust也允许使用impl关键字来绑定方法和函数

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String),
    Transformer(String),
    GNN(String)
}

impl NeuralNetwork {
    fn make_cnn(s: String) -> NeuralNetwork{ //绑定一个函数,用于创建CNN类型枚举
        NeuralNetwork::CNN(s)
    }
}

fn main(){
    let nn1 = NeuralNetwork::make_cnn(String::from("TextCNN"));
    let nn2 = NeuralNetwork::Transformer(String::from("Transformer-XL"));

}

match匹配枚举

match的语法大致如下

match 变量{
结果1 => 表达式1,
结果2 => 表达式2,
_ => 剩余结果处理
}

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String)
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN"));
    match nn1 {
        NeuralNetwork::CNN(s) => println!("CNN 变体为 {}", s), //匹配到对应类型,后面写表达式,而括号内的s就是枚举所绑定类型
        NeuralNetwork::RNN(s) => println!("RNN 变体为 {}", s),
        NeuralNetwork::GAN(s) => println!("GAN 变体为 {}", s)
    }
}

需要注意的是match需要把每一种结果都列出来,如果剩下的不想列可以使用通配符_来表示.

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String)
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN"));
    match nn1 {
        NeuralNetwork::CNN(s) => println!("CNN 变体为 {}", s),
        _ => println!("非CNN") //剩余类型统一处理
    }
}

如果箭头后面需要处理更复杂的逻辑,则可以用函数块来写,如下

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String)
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN"));
    match nn1 {
        NeuralNetwork::CNN(s) => {
            let len = s.len();
            println!("CNN 变体为 {} {}", s, len);
        },
        _ => println!("非CNN");
    }
}

除此之外match其实是有返回值的,正如函数块的最后一个运算式为返回值一样,match的返回值为=>后的值

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String)
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN"));
    let res = match nn1 {
        NeuralNetwork::CNN(s) => {
            println!("CNN 变体为 {}", s);
            s //s是返回值,返回给res,下面同理
        },
        NeuralNetwork::RNN(s) => {
            println!("RNN 变体为 {}", s);
            s
        },
        NeuralNetwork::GAN(s) => {
            println!("GAN 变体为 {}", s);
            s
        }
    };
    println!("res is {}", res)
}

除了枚举之外,match也可以匹配别的值

fn main(){
    let x = 10;
    match x {
        1 => println!("Is one!"),
        2 => println!("Is tow"),
        _ => println!("Other number!")
    };
}
fn main(){
    let x = "adasdasd";
    match x {
        "abc" => println!("Is abc!"),
        "efg" => println!("Is efg"),
        _ => println!("Other string!")
    };
}

if let语句

如果指向match一种情况,则可以使用if let这个语法糖,只针对一种模式进行匹配

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String)
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN"));
    if let nn1 = NeuralNetwork::CNN{
    	println!("是CNN");
    }
}

注意,if let后面的判断是=,而不是==。如果想要处理剩余情况,可以再加一个else

enum NeuralNetwork {
    CNN(String),
    RNN(String),
    GAN(String)
}

fn main(){
    let nn1 = NeuralNetwork::CNN(String::from("TextCNN"));
    if let nn1 = NeuralNetwork::CNN{
    	println!("是CNN");
    }else{
		println!("不是CNN");
	}
}

Option

Option是一种枚举,包含两种类型,分别是Some和None。Rust设计Option就是为了避免None和其它的值做运算而造成一系列错误而设计的。
Option的定义大致如下:

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

Option可以如下使用

fn main(){
    let x = Option::Some("hello");
    let y: Option<&str>= Option::None; //这里属于泛型,需要指定Option的泛型类型
    let z = x + y; // None无法和y运算,从而保证了安全性
}
fn main(){
    let _x = Option::Some("hello");
    let y: Option<&str> = None;
    match y {
        Option::None => println!("空的"),
        Option::Some(t) => println!("非空, 是{}", t)
    }
}

match pattern

基本pattern

match允许更为复杂的匹配,这称之为匹配的pattern,其中常用的操作如下:

  1. | 用于分割多个可能,Rust会从左到右依次检查是否符合

例如:a | b | c则会 检查a后检查b,再检查是否符合c

  1. ()元组用于实现匹配坐标这类的多个参数数据
  2. 可以用…=b, a…=b这种Range来进行范围的匹配

注意,a…b是左闭右开类型,而加上等号则是左闭右闭

  1. _可以用来匹配剩余未匹配的所有值
fn main(){
    let x: i32 = 100;
    match x {
        1 | 2 | 3 => println!("x位于1到3之间"), //会依次检查x是1,2还是3.只要匹配一个就进入后面的语句
        4 ..=9 => println!("x位于4到9之间"),
        10 | 100..=1000 => println!("x为10,或者100-1000之间的数"),
        _ => println!("x不是所期待的数字")
    };
}
fn main(){
    let x: (i32, i32) = (-9, -10);
    match x {
        (1, 1) => println!("点(1, 1)"),
        (1, 2 | 3 | 4) => println!("点(1,2), (1,3)或((1,4))"),
        (..=-1, ..=-1) => println!("第三象限的点"),
        _ => println!("其他区域的点")
    };
}

可以使用@符号把必要时的某个值绑定到变量上,操作为 变量 @ 匹配式

fn main(){
    let x: (i32, i32) = (-9, -10);
    match x {
        (1, 1) => println!("点(1, 1)"),
        (1, 2 | 3 | 4) => println!("点(1,2), (1,3)或((1,4))"),
        (x @ ..=-1, y @ ..=-1) => println!("第三象限的点({}, {})", x, y), //绑定第一个空的值到x,同理绑定y。
        _ => println!("其他区域的点")
    };
}
pattern守卫

pattern后面可以加上if判断语句来进一步的判断这个匹配是否合法,示例如下

fn judge_prime(x: i32) -> bool{
	// TODO: 判断素数
    true
}
fn main(){
    let tmp: (i32, i32) = (-9, -10);
    match tmp {
        (x @ 0..=10, y @ _) if x == y => println!("在y=x直线上(0 <= x <= 100)"),
        (x @ 0..=100, y @ 0..=100) if judge_prime(x) && judge_prime(y) => println!("(x, y)为0-100内的素数坐标"),
        _ => println!("")
    };
}

当然if后面的语句可以替换成函数块{}只要其返回值是bool类型即可。

fn judge_prime(_x: i32) -> bool{
	// TODO: 判断素数
    true
}

fn main(){
    let tmp: (i32, i32) = (-9, -10);
    match tmp {
        (x, y) if x == y => println!("在y=x直线上"), //此时x,y无绑定,也就是无绑定上的约束
        (x @ 0..=100, y @ 0..=100) if {
            let tmp: bool = judge_prime(x);
            tmp && judge_prime(y)
        }
             => println!("(x, y)为0-100内的素数坐标"),
        _ => println!("")
    };
}

泛型

如其他语言(CPP,JAVA)一样,Rust也支持泛型。

泛型函数

泛型函数需要在函数名上指明泛型类型,通常用T,U这种大写字母来表示

fn qpow<T>(base: T, p: i32) -> T{
    //TODO实现快速幂的逻辑
}

上述函数中T表示某个抽象的类型,而这个类型需要在函数名部分标注出来。随后的base和返回值都是T类型。
当然涉及到两个或者多个翻新类型时,需要都在函数名后面声明出

fn Test<T, U>(x: T, y: U) -> (T, U){
	//TODO
}

泛型结构体

与函数类似,可以在结构体明上声明某个或者多个抽象类型

struct Point<T>{
    x: T,
    y: T
}
fn main(){
    let ip = Point{x: 10, y: 10}; //此时ip类型为Point<i32>
    let fp = Point{x: 10.0, y: 11.1};//此时ip类型为Point<f64>
}

注意,Rust在编译时会把所有的抽象类型T替换成具体的类型,因为Rust是静态的,所以在编译之后所有的抽象类型都已经有具体的确定类型的值了。

struct Point<T>{
    x: T,
    y: T
}
fn main(){
    let ip: Point<i64> = Point{x: 10, y: 10}; //强制约束为i64
    let fp = Point{x: 10.0, y: 11};//报错,因为有两个类型
}

泛型枚举

枚举的泛型就是把其变体所绑定的类型内添加泛型,前面的Option的Some就是这个原理。

enum Test<T, U> {
    SOME(T, U),
    OTHER(T),
    NO

为结构体绑定泛型方法

为结构体绑定方法使用impl关键字,如果该结构体是一个泛型结构体,则需要再impl后面加表明抽象类型。

struct Point<T>{
    x: T,
    y: T    
}

impl Point<i32> { //只为i32类型实现判断素数坐标的方法, 由于是实现具体类型所以不需要再impl后面加尖括号
    fn judge_prime(&self) -> bool{
        //TODO判断素数
    }
}

impl<T> Point<T> { //为所有类型都实现一个画图的方法,抽象方法要加尖括号表明抽象类型
    fn show_point(&self) -> (){
        //TODO画图
    }
}

注意,此时impl的泛型参数不影响具体方法的参数

struct Point<T, U>{
    x: T,
    y: U
}

impl<T, U> Point<T, U> {
    fn make_new_point<W>(&self, other_point: Point<T, W>) -> Point<T, W>{
        Point { 
            x: self.x.clone(),  //这里会有报错,具体原因看后面的trait部分
            y: other_point.y 
        }
    }
}

fn main(){
}

trait

trait的定义与实现

trait是Rust的一个类似于接口的类型,他可以为enum,struct来定义一套标准的接口,或者默认的某些方法。

trait 名字{
函数|抽象函数
}

看下面的例子

trait Draw {
    fn _draw_atom(&self) ->(); //实现单个点的绘画,是一个抽象的方法
    fn draw(&self) -> (){ //draw绘画调用_draw_atom,是一个已经实现的方法
        self._draw_atom()
    }
}

使用

impl trait类型 for 结构体|枚举

来为结构体或者枚举绑定方法

struct Point{
    x: f64,
    y: f64
}

struct Line{
    A: f64,
    B: f64
}

trait Draw {
    fn _draw_atom(&self) ->();
    fn draw(&self) -> (){ //该方法为所有实现Draw trait的结构体/枚举所共有

        self._draw_atom()
    }
}

impl Draw for Point { //为Point结构体实现
    fn _draw_atom(&self) ->() {
        println!("{} {}", self.x, self.y);
    }
}

impl Draw for Line {//为Line结构体实现
    fn _draw_atom(&self) -> (){
        println!("{}x + {}y = 0", self.A, self.B);
    }
}

用trait指定定特定方法

首先对于简单的约束,我们可以直接在函数参数后面加上 impl trait类型 的方式来要求某个参数必须实现特定的trait

fn Test(x: impl Draw){ //约束x必须实现Draw
}

如果类型比较多,且复杂则可以使用Bound的方式具体做法如下

在函数名后面用尖括号,写上泛型,然后后面用冒号指定trait
函数名<T: trait1 + traiit2+…, U: trait1+traitk…>

use std::fmt::Display; //使用fmt内的Display trait
fn Test<T: Draw + Display, U: Display>(x: T, y: U){} 
//要求x实现了Draw和Display两个trait,而y只要求实现Display这个trait

此时你会发现bound约束太长了,降低了函数的可读性。于是Rust允许使用where关键字把Bound约束后置, 具体做法如下

  1. 在函数尖括号后声明泛型,T,U等变量。
  2. 在函数的函数体前用where关键字,后面跟上每个泛型变量的约束trait
use std::fmt::Display;
fn Test<T, U>(x: T, y: U)
where
	T: Draw + Display, //约束T
	U: Display //约束U
{
	//TODO:函数体
} 

我们同样可以再返回类型上约束实现具体的trait。但此时需要注意,返回的类型必须要是一种类型,不能使用分支语句使其返回多种可能的类型

use std::fmt::Display;
fn Test<T, U>(x: T, y: U) -> U //返回类型必须实现了Display
where
	T: Draw + Display, //约束T
	U: Display //约束U
{
	//TODO:函数体
} 

impl中的trait约束

除此之外,在impl中的泛型T,也可以进行相对应的trait的约束

impl<T: Display> Point<T>{ 
//对所有实现了Display trait的类型T,其Point<T>都会具有test方法
	fn test(&self){
	}

}

trait可以进行覆盖实现,也就是为所有实现某些trait的类型添加一些方法文章来源地址https://www.toymoban.com/news/detail-644245.html


trait Hello {
    fn print_hello(){
		println!("hello");
	}
}

impl<T: std::fmt::Display> Hello for T{
	//为所有实现了Display这个trait的类型都添加一个print_hello函数

}
fn main(){
    i32::print_hello(); //此时实现了Display的i32类型,也可以调用这个方法了
}

到了这里,关于Rust语法: 枚举,泛型,trait的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Rust 基础篇】Rust Sized Trait:理解Sized Trait与动态大小类型

    Rust是一门以安全性和性能著称的系统级编程语言。在Rust中,类型大小的确定在编译期是非常重要的。然而,有些类型的大小在编译期是无法确定的,这就涉及到了Rust中的动态大小类型(DST)。为了保证在编译期可以确定类型的大小,Rust引入了Sized trait。本篇博客将深入探讨

    2024年02月14日
    浏览(37)
  • 【Rust 基础篇】Rust Deref Trait 的使用

    在 Rust 中,Deref trait 是一种特殊的 trait,用于重载解引用操作符 * 。通过实现 Deref trait,我们可以定义类型的解引用行为,使其在使用 * 运算符时表现得像引用类型。 本篇博客将详细介绍 Rust 中如何实现和使用 Deref trait,以及它在代码中的应用场景。 Deref trait 的定义如下:

    2024年02月17日
    浏览(38)
  • 【Rust 基础篇】Rust Trait 实现:灵活的接口抽象

    Rust是一种以安全性和高效性著称的系统级编程语言,其设计哲学是在不损失性能的前提下,保障代码的内存安全和线程安全。为了实现这一目标,Rust引入了\\\"所有权系统\\\"、\\\"借用检查器\\\"等特性,有效地避免了常见的内存安全问题。然而,在编程中我们常常需要实现多态和抽象

    2024年02月15日
    浏览(37)
  • 【Rust 基础篇】Rust派生宏:自动实现trait的魔法

    Rust是一门现代的、安全的系统级编程语言,它提供了丰富的元编程特性,其中派生宏(Derive Macros)是其中之一。派生宏允许开发者自定义类型上的trait实现,从而在编译期间自动实现trait。在本篇博客中,我们将深入探讨Rust中的派生宏,包括派生宏的定义、使用方法以及一些

    2024年02月14日
    浏览(31)
  • 研读Rust圣经解析——Rust learn-16(高级trait,宏)

    我们使用type即可声明一个关联类型,关联类型的作用就是简化和隐藏显示类型(个人认为) 简化:一个很长的类型总是被需要时,需要开发者耗费精力的重复书写,而且若有改动,则需要改多个地方 隐藏:对外部调用者隐藏,外部调用者无需知道它指的是什么,只要

    2024年02月02日
    浏览(67)
  • Rust-trait

    Rust语言中的trait是非常重要的概念。 在Rust中,trait这一个概念承担了多种职责。在中文里,trait可以翻译为“特征”“特点”“特性”等。 trait中可以定义函数。用例子来说明,我们定义如下的trait: 上面这个trait包含了一个方法,这个方法只有一个参数,这个self参数是什么意

    2024年01月20日
    浏览(43)
  • Rust 程序设计语言学习——基础语法

    Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软

    2024年01月22日
    浏览(52)
  • 30天拿下Rust之Trait

    概述         在Rust中,Trait是一个核心概念,它允许我们定义类型应该具有的行为。Trait类似于其他语言中的接口,但Rust的Trait更为强大和灵活。它不仅定义了一组方法,还允许我们指定方法的默认实现、泛型约束和继承。通过Trait,我们可以定义一组方法的签名和关联类

    2024年03月17日
    浏览(37)
  • Rust系列(四) trait备忘录(持续更新)

    上一篇:Rust系列(三) 类型系统与trait 基于官方文档进行简单学习记录,保证所有示例是可运行的基本单元。测试 rust 程序除了使用官方的 playground 之外,还可以通过定义 [[example]] 来运行程序。 用于 不可变对象 的解引用操作,语法类似 *v 。 官方文档: https://doc.rust-lang.org

    2024年02月14日
    浏览(33)
  • 【社区投稿】给Rust的Struct自动实现trait

    我们通常使用 这样的方式给struct自动实现相应的trait,从而让struct具备某些特性,但是如果我们想让编译器给struct自动实现自己定义的trait要怎么办? 首先我们需要有一个trait,假设如下面的定义: 我们定义这个trait给struct赋予一个行为是逐行打印struct的所有Field。当然如果是

    2024年02月20日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包