[易学易懂系列|rustlang语言|零基础|快速入门|(14)]

有意思的基础知识

Impls & Traits实现与特征

我之前说到的struct结构体,其实就类似于面向对象语言中的类class。

但这个struct,并没有定义方法或函数。

那要怎么办呢?

Rust用关键词impls(实现)来定义struct和enum的方法或函数。

而trait(特征),类似于面向对象语言中的接口interface。

特征,是用来定义要实现的方法,一个类型可以有多个特征。特征可以有默认实现函数,这个默认函数可以在运行时重写。

我们来看看代码:

1.没有trait特征的impl实现:

struct Player {
    first_name: String,
    last_name: String,
}

impl Player {

    fn full_name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

fn main() {
    let player_1 = Player {
        first_name: "Rafael".to_string(),
        last_name: "Nadal".to_string(),
    };

    println!("Player 01: {}", player_1.full_name());
}

// ?? Implementation must appear in the same crate as the self type

// ?? And also in Rust, new traits can be implemented for existing types even for types like i8, f64 and etc.
// Same way existing traits can be implemented for new types you are creating.
// But we can not implement existing traits into existing types.

我们来看看上面的代码,其中这段代码,跟之前的函数,有点不一样:

impl Player {
    fn full_name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

这里第一个参数:&self,它代表什么意思呢?

&self代表结构体Player的实例。

当然这里的第一个参数,可以是:self, &self, 或&mut self

self是一个栈内值 。

&self是一个引用值(不可变),数据在堆heap里分配空间。

&mut self是一个可变的引用值,数据在堆heap里分配空间

所以通过,self.first_name, self.last_name,我们就可以访问结构体的属性first_name,last_name。

这里的impl 代码块代表结构体Player的具体实现方法,impl后面的类型,必须是Player。

1.特征(trait)的实现方式:

struct Player {
    first_name: String,
    last_name: String,
}

trait FullName {
    fn full_name(&self) -> String;
}

impl FullName for Player {
    fn full_name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

fn main() {
    let player_2 = Player {
        first_name: "Roger".to_string(),
        last_name: "Federer".to_string(),
    };

    println!("Player 02: {}", player_2.full_name());
}

// ?? Other than functions, traits can contain constants and types.

我们可以看到上面代码中加了一个特征的定义,代码可读性好很多:

trait FullName {
    fn full_name(&self) -> String;
}

好,现在我们加上默认函数(default method):

struct Player {
    first_name: String,
    last_name: String,
}
trait FullName {
    fn full_name(&self) -> String;
    fn baz(&self) { println!("We called baz.");// 默认函数
}
impl FullName for Player {
    fn full_name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

fn main() {
    let player_2 = Player {
        first_name: "Roger".to_string(),
        last_name: "Federer".to_string(),
    };

    println!("Player 02: {}", player_2.full_name());
    player_2.baz();//直接调用默认函数
}

3.关联函数(Associated functions):

请看如下例子:

struct Player {
    first_name: String,
    last_name: String,
}

impl Player {
    fn new(first_name: String, last_name: String) -> Player {
        Player {
            first_name : first_name,
            last_name : last_name,
        }
    }

    fn full_name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

fn main() {
    let player_name = Player::new("Serena".to_string(), "Williams".to_string()).full_name();
    println!("Player: {}", player_name);
}

// We have used :: notation for `new()` and . notation for `full_name()`

// ?? Also in here, instead of using new() and full_name() separately as two expressions,
// we can use Method Chaining. ex. `player.add_points(2).get_point_count();`

上面代码,我们看到多了一个函数new(注意这个函数的参数,不用self关键字),我们看到这个new函数跟java中的构造函数很像。

这个函数可以不通过创建对象实例,就可以直接调用,一般用双冒号::,如:Player::new。

在Rust,我们叫这种函数为:关联函数(Associated functions)。

我们再来看看特征的其他用法:

带泛型的特征:

trait From<T> {
    fn from(T) -> Self;
}
    impl From<u8> for u16 {
        //...
    }
    impl From<u8> for u32{
        //...
    }

// Should specify after the trait name like generic functions

带继承关系的特征:

trait Person {
    fn full_name(&self) -> String;
}
trait Expat{
    fn tax(&self)->f64;
}

    trait Employee : Person { // Employee inherits from person trait
      fn job_title(&self) -> String;
    }

    trait ExpatEmployee : Employee + Expat { // ExpatEmployee inherits from Employee and Expat traits
      fn additional_tax(&self) -> f64;
    }

特征对象:

trait GetSound {
    fn get_sound(&self) -> String;
}

struct Cat {
    sound: String,
}
    impl GetSound for Cat {
        fn get_sound(&self) -> String {
            self.sound.clone()
        }
    }

struct Bell {
    sound: String,
}
    impl GetSound for Bell {
        fn get_sound(&self) -> String {
            self.sound.clone()
        }
    }

fn make_sound<T: GetSound>(t: &T) {
    println!("{}!", t.get_sound())
}

fn main() {
    let kitty = Cat { sound: "Meow".to_string() };
    let the_bell = Bell { sound: "Ding Dong".to_string() };

    make_sound(&kitty); // Meow!
    make_sound(&the_bell); // Ding Dong!
}

上面的代码我们可以看到,Rust实现多态,可以用动态分发的机制,所谓动态分发,就是在运行时,才决定真正的实现,并加载这个实现。

以上,希望对你有用。

如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust

参考:https://doc.rust-lang.org/rust-by-example/generics/impl.html

? https://learning-rust.github.io/docs/b5.impls_and_traits.html

? https://doc.rust-lang.org/book/ch05-03-method-syntax.html

原文地址:https://www.cnblogs.com/gyc567/p/11969589.html

时间: 2024-08-20 00:40:16

[易学易懂系列|rustlang语言|零基础|快速入门|(14)]的相关文章

[易学易懂系列|rustlang语言|零基础|快速入门|(12)]

有意思的基础知识 Enums 今天我们来讲讲枚举. 在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数.这两种类型经常(但不总是)重叠. 是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY.MONDAY.TUESDAY.WEDNESDAY.THURSDAY.FRIDAY.SATURDAY就是一个枚举. 如下定义: enum Day { Sunday, Monday, Tuesday, Wednesday, Thu

[易学易懂系列|rustlang语言|零基础|快速入门|(13)]

有意思的基础知识 Generics泛型 我们今天来看看泛型. 什么是泛型? 我们来看看这样的情景: 我们要写一个函数,这个函数可以处理不同类型的值,但这个值的类型,在运行时,才由调用者确定. 我们不可能在函数方法中,一开始就写死. 那要什么办? 用泛型. 比如:用x : T替换x : u8 我们来看看例子: 泛型函数: fn takes_anything<T>(x: T) { // x has type T, T is a generic type } fn takes_two_of_the_

[易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针]

实用知识 智能指针 我们今天来讲讲Rust中的智能指针. 什么是指针? 在Rust,指针(普通指针),就是保存内存地址的值.这个值,指向堆heap的地址. 什么是智能指针? 在Rust,简单来说,相对普通指针,智能指针,除了保存内存地址外,还有额外的其他属性或元数据. 在Rust中,因为有所有权和借用的概念,所以引用和智能指针,又有一点不一样. 简单来说,智能指针,拥有数据所有权,而引用没有. 智能指针分以下几种: 1.Box,用于在堆里分配内存. 2.Rc,引用计数类型,用于多线程中的多个所有

[易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]

项目实战 实战4:从零实现BTC区块链 我们今天来开发我们的BTC区块链系统. 简单来说,从数据结构的角度上来说,区块链,就是区块组成的链. 以下就是BTC区块链典型的结构: 那最小单元就是区块:block. 这个block包含两部分:区块头,区块体. 我们先忽略Merkle树,先简化所有数据结构,只保留最基本的数据结构. 那区块头,就包含:时间截:前一个区块地址 区块体,就包含交易数据,我们用一个vector来存储. 代码如下 : ///交易结构体 #[derive(Clone, Hash,

[易学易懂系列|rustlang语言|零基础|快速入门|(29)|实战6:BDD工具cucumber_rust]

项目实战 实战6:BDD工具cucumber_rust 今天我们来学习下BDD行为驱动测试工具cucumber_rust. 关于BDD,可以简单看看这些介绍: https://www.cnblogs.com/superhin/p/11454716.html#bdd%E4%BB%8B%E7%BB%8D https://www.jianshu.com/p/0389360ac58f https://www.infoq.cn/article/2011/02/BDD-ATDD 简单来说,BDD是一种更好,

[易学易懂系列|rustlang语言|零基础|快速入门|(17)|装箱crates]

实用知识 装箱crates 我们今天来讲讲装箱技术crates. 什么是crates? 英语翻译是: 英 [kre?t] 美 [kre?t] n. 板条箱:篓 vt. 将某物装入大木箱或板条箱中 [ 过去式 crated 过去分词 crated 现在分词 crating 复数 crates 第三人称单数 crates ] 其实,它也就是一种模块化封装技术. 我们还是来看看代码,我们先用命令:cargo new greetings 生成一个新的工程,工程目录如下 : // # It generat

[易学易懂系列|rustlang语言|零基础|快速入门|(18)|use关键词]

实用知识 use关键词 我们今天来讲讲use关键词. 1.简单来说,use是给其他方法或资源定义一个别名,然后调用者,就可以直接用这个别名来调用,从而简化代码. 看下例子吧,我们先来看看没有用use的代码: // -- Initial code without the `use` keyword -- mod phrases { pub mod greetings { pub fn hello() { println!("Hello, world!"); } } } fn main()

小D课堂 - 零基础入门SpringBoot2.X到实战_第1节零基础快速入门SpringBoot2.0_1、SpringBoot2.x课程介绍和高手系列知识点

1 ======================1.零基础快速入门SpringBoot2.0 5节课 =========================== 1.SpringBoot2.x课程全套介绍和高手系列知识点     简介:介绍SpringBoot2.x课程大纲章节         java基础,jdk环境,maven基础 2.SpringBoot2.x依赖环境和版本新特性说明 简介:讲解新版本依赖环境和springboot2新特性概述 1.依赖版本jdk8以上, Springboot2

零基础快速入门web学习路线(含视频教程)

下面小编专门为广大web学习爱好者汇总了一条完整的自学线路:零基础快速入门web学习路线(含视频教程)(绝对纯干货)适合初学者的最新WEB前端学习路线汇总! 在当下来说web前端开发工程师可谓是高福利.高薪水的职业了.所以现在学习web前端开发的技术人员也是日益增多了,但是在学习web前端开发中盲目的去学习而没有一个完整的思路和学习路线也是不行的. 成为一个合格的web前端开发工程师的具备什么条件? 熟练的掌握HTML.CSS.JS.JQ等最基本的技术. 现在,只掌握这些已经远远不够了.无论是开