APP下载

Rust | 思想:Rust编程课要点笔记

原创

Rust

业务驱动技术,技术衍生思想~

编程技巧

  1. 宏编程并没有什么大不了的,抛开 quote/unquote,它主要的工作就是把一棵语法树转换成另一颗语法树,而这个转换的过程深入下去,不过就是数据结构到数据结构的转换而已。所以一句话总结:宏编程的主要流程就是实现若干 F rom 和 TryFrom,是不是很简单。

  2. 在 Rust 中,通过 Trait,可以很方便做 控制反转(Inversion of Control)。

IoC 也可以理解为把流程的控制从应用程序转移到框架之中。以前,应用程序掌握整个处理流程;现在,控制权转移到了框架,框架利用一个引擎驱动整个流程的执行,框架会以相应的形式提供一系列的扩展点,应用程序则通过定义扩展的方式实现对流程某个环节的定制,“框架Call应用”。基于MVC的web应用程序就是如此。

  1. 一般我们在做一个库的时候,不会把内部使用的数据结构暴露出去,而是会用自己的数据结构包裹它。

  2. 关注点分离(Separation of Concerns),是控制软件复杂度的法宝。Rust 标准库中那些经过千锤百炼的 trait,就是用来帮助我们写出更好的、复杂度更低的代码。

编程思想

  1. 软件领域有个著名的格林斯潘第十定律:

任何 C 或 Fortran 程序复杂到一定程度之后,都会包含一个临时开发的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的 Common Lisp 实现。

类比:

任何 API 接口复杂到一定程度后,都会包含一个临时开发的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的 SQL 实现。

  1. 数据转换是编程活动中非常重要的部分,我们日常写代码时,主要在处理什么?绝大多数处理逻辑是把数据从一个接口转换成另一个接口。

  2. 好的代码,应该是每个主流程都清晰简约,代码恰到好处地出现在那里,让人不需要注释也能明白作者在写什么。

这意味着,我们要把那些并不重要的细节封装在单独的地方,封装的粒度一次写完、基本不需要再改动为最佳,或者即使改动,它的影响也非常局部。

这样的代码,方便阅读、容易测试、维护简单,处理起来更是一种享受。Rust 标准库的 From/TryFrom ,就是处于这个目的设计的,非常值得我们好好使用。

  1. 优秀的设计一定是产生简单易读的代码,而不是相反。

  2. 作为开发者,在工作中常常能体会到:恰到好处地限制,反而会释放无穷的创意和生产力。

类型

数据类型

array

数组,固定大小的 同构序列,语法:[T; N]

bool

布尔值,true, false

char

utf-8 字符

fn

函数指针,示例:fn(&str) -> usize

i8/i16/i32/i64/i128/isize

有符号整数

u8/u16/u32/u64/u128/usize

无符号整数

pointer

裸指针,*const T, *mut T,注意:裸指针在解引用时是不安全的

示例:

               
  • 1
  • 2
  • 3
  • 4
  • 5
let x = 42; let mut y = 24; let raw = &x as *const i32; let raw_mut = &mut y as *mut i32; COPY

reference

引用,&T, &mut T

slice

切片,动态大小的连续序列,用[T]表述。

一般使用其引用&[T], &mut[T] 或 Box<[T]>

str

字符串切片,一般使用其引用&str, &mut str

tuple

元组,固定大小的异构序列,表述为(T, U, ...)

unit

() 类型,表示没有值

never

!类型,表示类型无法产生任何值。注意:目前还是实验特性

示例:

               
  • 1
  • 2
Result<T, !> # 永远不会出错 Result<!, ConnectionError> # 一个循环要么出错退出,要么永不返回 COPY

组合类型 和 自定义类型

Box<T>

分配在堆上的类型 T

Option<T>

T要么存在,要么为None

Result<T, E>

要么成功 Ok(T), 要么失败Err(E)

示例:

               
  • 1
  • 2
  • 3
Ok(42) Err(ConnectionError::TooMany) COPY

Vec<T>

可变列表,分配在堆上

String

字符串

HashMap<K, V>

哈希表

示例:

               
  • 1
let map: HashMap<&str, &str> = HashMap::new(); COPY

HashSet<T>

集合

示例:

               
  • 1
let set: HashSet<u32> = HashSet::new(); COPY

RefCell<T>

为 T 提供内部可变性的智能指针。

示例:

               
  • 1
  • 2
  • 3
let v = RefCell::new(42); let mut borrowed = v.borrow_mut(); COPY

Rc<T> / Arc<T>

为T 提供引用计数的智能指针

struct

结构体

enum

标签联合

类型推导

  1. Rust 编译器需要足够的上下文来进行类型推导, 所以有些情况下,编译器无法推导出合适的类型。

  2. 在泛型函数后使用::<T> 来强制使用类型T, 这种写法被称为 turbofish.

泛型函数

  1. 定义:

在声明一个函数时,可以不指定具体的参数或返回值的类型,由泛型参数来代替, 对函数而言,这是更高阶的抽象。

  1. 泛型函数,Rust会进行单态化(monomorphization)处理,在编译时,把所有用到的泛型函数的泛型参数展开,生成若干个函数。

单态化的好处是,泛型函数的调用是静态分派(static dispatch),在编译时就一一对应,既保有多态的灵活性,又没有任何效率的损失,和普通函数调用一样高效。

单态化的不足是:

  • 编译速度很慢,一个泛型函数,编译器需要找到所有用到的不同类型,一个个编译,所以 Rust 编译代码的速度总被人吐槽,这和单态化脱不开干系(另一个重要因素是宏)
  • 单态化,代码以二进制分发会损失泛型的信息;

要点

  1. 在给类型添加约束时,可以逐步添加,可以让约束只出现在它不得不出现的地方,这样代码的灵活性最大。

  2. 任何语言的学习离不开:「精准学习 + 刻意练习」。

精准学习:

  • 深挖一个个高大上的表层知识点,回归底层基础知识的本原,再使用「类比、联想」等方法,打通涉及的基础知识;
  • 从「底层设计」往表层实现,一层层构建知识体系,这样「撒一层土,夯实,再撒一层」,让你对知识点理解得「更透彻、掌握得牢固」。
  1. (回归本原的重要性)第一性原理: 回归事物最基础的条件,将其拆分成基本要素解构分析,来探索要解决的问题。

值在内存中的访问规则:堆 和 栈

  1. 通过练习发现学习过程中的「不自知问题」,让自己从「我不知道我不知道」走向「我知道我不知道」,并在下一个循环中弥补知识的漏洞。

  2. 治学的方法:博学之,审问之,慎思之,明辨之,笃行之 —— 《中庸》— 子思。

  3. 成长环:学习 — 构建 — 反思。

  4. 学好任意一门编程语言:首先要吃透涉及的概念,因为编程语言,不过是这些概念的「具体表述和载体」。

  5. 算法 + 数据结构 = 程序 —— Pascal Creator, Niklaus Wirth(尼古拉斯·沃斯,图灵奖得主)。

  6. 深度掌握「类型系统」,才能随心所欲地使用 Rust 为系统构建数据结构。

  7. 在Rust中,可以:

  • 使用 Trait 做接口设计;
  • 使用泛型做编译期「多态」;
  • 使用 Trait Object 做「运行时多态」。

tips:用好Trait 和 泛型,可以非常高效地解决复杂的问题。

  1. unsafe rust:

所谓 unsafe,不过是把 Rust 编译器在编译器做的严格检查退步成为 C++ 的样子,由开发者自己为其所撰写的代码的正确性做担保。

  1. FFI,rust 和 其它语言互通操作的桥梁。

掌握好 FFI,你就可以用 Rust 为你的 Python/JavaScript/Elixir/Swift 等主力语言在关键路径上提供更高的性能,也能很方便地引入 Rust 生态中特定的库。

  1. Fearless Concurrency:
  • atomics
  • mutex
  • Semaphore
  • Channel
  • actor model
  1. 想真正把语言融会贯通,还要靠大风大浪中的磨炼。

参考资料

  1. 格林斯潘第十定律 - https://zh.wikipedia.org/wiki/格林斯潘第十定律 

  2. IoC - https://zh.wikipedia.org/wiki/控制反转 

评论区

写评论

登录

所以,就随便说点什么吧...

这里什么都没有,快来评论吧...