rust入坑小记-12-深入类型与宏
6 深入类型 6.1 类型转换 使用as转换 rust不支持不同类型之间的比较: 12345678fn main() { let a: i32 = 10; let b: u16 = 100; if a < b { println!("Ten is less than one hundred."); } } 为了让它们可以比较,可以使用 as 关键字进行显式类型转换(casting): 12345678fn main() { let a: i32 = 10; let b: u16 = 100; if a < b as i32 { println!("Ten is less than one hundred."); } } 在这个例子中,需要将b显式转换为i32而不是将a转换为u16,这是因为每个类型能表达的数据范围不同,如果把范围较大的类型转换成较小的类型,比如: 12345fn main() { // 0000 0000 0000 0000 0000 0001 0000 0100 ...
rust入坑小记-11-面向对象与不安全
4 rust与面向对象式编程 面向对象编程(Object-Oriented Programming,OOP)是一种模式化编程方式。现代众多语言基本都支持面向对象范式,例如C++、Java、python等等。rust则并不完全是面向对象的,在一些定义下,rust是面向对象的,在其他定义下,rust则不是。至于rust到底遵循哪一种编程范式,至今仍有争论。rust是基于表达式的编程语言,但它也是面向过程的,同时提供了函数式编程的一些特性。这里并不会过多地讨论rust到底遵循哪一种确定的范式,就像我在Go面向对象中提到的:面向对象只是一种实现形式,本章主要就是介绍如何在rust中实现面向对象设计模式,并讨论这么做与利用rust自身的一些优势实现的方案相比有什么取舍。 4.1 面向对象语言的特点 面向对象的特性为:封装、继承、多态。另外,对象(Object)也是整个理念的重要概念,它来源于 20 世纪 60 年代的 Simula 编程语言。 对象包含数据和行为 由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides(Addison-W ...
rust入坑小记-10-并发编程
3 并发编程 不同的编程语言使用不同的线程模型,rust标准库使用1:1线程实现,这代表程序的每一个语言级线程使用一个系统线程。在线程间通信方面,rust提供了不同程度抽象的工具,比如通道、互斥锁和原子类型,我们会在后面分别介绍它们。 3.1 多线程同时运行代码 为了创建一个新线程,需要调用 thread::spawn 函数并传递一个闭包,并在其中包含希望在新线程运行的代码: 123456789101112131415161718use std::thread;use std::time::Duration;fn main() { thread::spawn(|| { for i in 1..10 { println!("hi number {} from the spawned thread!", i); // 强制线程停止执行一小段时间(1ms) thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { ...
rust入坑小记-09-函数式编程与指针
三、rust进阶篇 1 rust与函数式编程 rust的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是函数式编程(functional programming)。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。 我们不会讨论函数式编程是或不是什么的问题,而是展示rust的一些在功能上与其他被认为是函数式语言类似的特性。主要内容如下: 闭包(Closures),一个可以储存在变量里的类似函数的结构。(这里的闭包是函数式编程概念的闭包) 迭代器(Iterators),一种处理元素序列的方式。 模式匹配 枚举 闭包和迭代器的性能。 在基础篇,我们已经介绍了其它受函数式风格影响的rust功能:模式匹配和枚举,因此本章的重点放在闭包和迭代器,掌握闭包和迭代器是编写符合语言风格的高性能rust代码的重要一环。 1.1 闭包 首先要说明的是,我们这里提到的闭包仅限于函数式编程概念上,而不是闭包最初始的定义。对于闭包的另一种解释,在我的文章编译原理中有介绍。其中有一些概念相同,而有一些则不同,我们这里主要讨论rust所定义的闭包。 在rust ...
rust入坑小记-08-生命周期
13 生命周期 生命周期用来保证所有的引用都是有效的,它实际上是另一类泛型。一个变量的生命周期在它创建的时候开始,在它销毁的时候结束。 在引用小节中我们遗漏了一个重要的细节:rust中的每一个引用都有其生命周期,也就是引用保持有效的作用域。大部分时候生命周期是隐含并可以推断的,正如大部分时候类型也是可以推断的一样。类似于当因为有多种可能类型的时候必须注明类型,也会出现引用的生命周期以一些不同方式相关联的情况,所以rust需要我们使用泛型生命周期参数来注明他们的关系,这样就能确保运行时实际使用的引用绝对是有效的。 13.1 悬垂引用 生命周期的主要目标是避免悬垂引用(dangling references): 1234567891011121314151617181920fn main() { let r; { let x = 5; r = &x; } println!("r: {}", r);}/* 错误如下: |6 | r = &x; | ^^ borrowed value ...
rust入坑小记-07-包管理与错误处理
11 package&crate 11.1 rustc编译单个文件 对于简单使用,可以通过rustc编译代码,假设当前文件为main.rs,内容为: 123fn main() { println!("Hello, world!");} 进行编译: 1rustc main.rs 在 Linux、macOS 或 Windows 的 PowerShell 上,在 shell 中输入 ls 命令可以看见这个可执行文件: 12$ lsmain main.rs 在 Linux 和 macOS,你会看到两个文件。在 Windows PowerShell 中,你会看到同使用 CMD 相同的三个文件。在 Windows 的 CMD 上,则输入如下内容: 12> dirmain.exe main.pdb main.rs 这展示了扩展名为.rs的源文件、可执行文件(在 Windows 下是main.exe,其它平台是main),以及当使用 CMD 时会有一个包含调试信息、扩展名为.pdb的文件。从这里开始运行main或main.exe文件,如下: 1$ ./main # W ...
rust入坑小记-06-集合类型
10 集合 集合(collections)是rust中非常有用的数据结构,下面会逐个介绍三种集合。 10.1 String 字符串切片 切片在前面介绍数组时有简单提到,它是一类引用,没有所有权。 1234let s = String::from("hello world");let hello = &s[0..5];let world = &s[6..11]; s是String类型,hello 没有引用整个 String s,而是引用了 s 的一部分内容,通过 [0..5] 的方式来指定。这就是创建切片的语法,使用方括号包括的一个序列:[开始索引..终止索引],其中开始索引是切片中第一个元素的索引位置,而终止索引是最后一个元素后面的索引位置,这是一个[)右半开区间。在切片数据结构内部会保存开始的位置和切片的长度,其中长度是通过 终止索引 - 开始索引 的方式计算得来的。 ..除了用于解构,在这里是生成连续序列的意思,如果你想从索引 0 开始,可以使用如下的方式,这两个是等效的: 12let slice = &s[0..2];let slice = &s ...
rust入坑小记-05-特征Trait
9 特征Trait 在之前,多次提到Copy、Debug等特征,特征和其他语言的接口很类似,trait 是对未知类型 Self 定义的方法集,如果不同的类型具有相同的行为,那么我们就可以定义一个特征,然后为这些类型实现该特征。定义特征是把一些方法组合在一起,目的是定义一个实现某些目标所必需的行为的集合。 9.1 定义特征 第一步是为类型定义特征,首先创建一个类型,以结构体为例: 1234struct Dog { color: String, age : u32,} 接下来,我们要定义一个特征: 123456trait Animal { // `Self` 是未知的类型 fn new(color: String, age: u32) -> Self; // `self` 是这个类型的实例 fn introduce(&self);} 你可以把它理解为go/java的接口,即你只需要声明函数签名,而不需要具体实现。使用trait关键字声明一个特征名为Animal,并定义要实现这个特征所需要的方法集,这里定义了new和introduce两个方法。 ...
rust入坑小记-04-方法与泛型
7 方法impl impl也可以叫做“实现”(implementation)。在面向对象语言中,方法一般是和类或者对象绑定的,rust在概念上来说也大差不差,但是方法的定义却并不在类中,而是在 impl 代码块中定义。 7.1 静态方法 首先是静态方法(这种定义在 impl 中且没有 self 的函数也叫做关联函数): 1234567891011121314151617181920struct Point { x: f64, y: f64,}impl Point { // 静态方法不需要self // 静态方法不需要被实例调用,并且是直接绑定到类型上 // 这类方法一般用作构造器(constructor) // 比如初始化一个(0,0)的点 fn origin() -> Point { Point { x: 0.0, y: 0.0 } } // 根据参数初始化一个(x,y)的点 // 在rust这一是个约定俗成的规则,使用 new 来作为构造器的名称 // 并且,出于设计上的考虑,rust 特地没有用 ...
rust入坑小记-03-函数与控制流
4 函数、语句与表达式 4.1 函数 函数的定义如下: 1234fn function(a:i32,b:i32) -> f64 {// body ...} 就这么简单。 与C或python不同的是,你无需关心函数定义在哪个位置(在前还是在后),rust中的函数都可以调用到。 4.2 语句与表达式 下面介绍语句与表达式。一般语句指的是以分号结尾的一些操作,在函数中,语句并不会返回值,比如: 12let a = 3;let a = a+1; 而表达式则是指没有分号的,并且会在求值后返回一个值,比如: 123let x = 5; // 语句x // 表达式x+1 // 表达式 在函数中,函数体包括一些语句+最后一行的零个或一个表达式。语句与表达式在写法上就差一个分号,表达式不能包含分号,因此,在函数结尾需要返回值的时候不能带分号,否则它就会变成一条语句,不会返回值。最后,如果不返回任何值,则会隐式地返回一个 ()。 当然,你也可以在函数体中使用return来返回值(return带分号和不带分号都可以): 1234fn foo() -> i32 { let ...