学习-程序君的Rust培训2

267 阅读4分钟

Move和Copy

let a = 10;
let b = vec![1, 2, 3];
{
  let (x, y) = (a, b);
}
let c = a; // a是i32基本类型,在大括号里a会copy给x,产生新的副本。它仍然有所有权
let d = b; // b已经被move给y,所以这里不能使用b
  1. 没有实现Copy trait时,默认是Move。即所有权移动
  2. 基本类型以及由基本类型构成的复杂类型,编译器会自动实现Copy
  3. 实现Copy trait时,复制,传参与函数返回值时是按位拷贝,不会转移所有权

Drop的顺序

{
  let s1 = "Hello world".to_string();
  // 这里s1move到了vec中
  let arr = vec![Box::new(s1)];
  // 先drop vec,再drop box,最后drop s1
  // 这里的先后是指调用先后,最后调用的被最先清理掉内存
}

生命周期短的先drop,生命周期长的后drop

Sized与DST

  • Sized: 编译期 - 可以放在栈上

    • Rust 有 Sized trait 和 ?Sized trait bound
    • 所有的泛型结构默认 T: Sized
  • DST: 运行期 - 一般只能放在堆上(stack_dst)

    • trait object(生成VTable)
    • [T] / str
    • struct / tuple 包含 DST(如:Mutex
    • 对于 DST,指针是 "fat pointer"
  • 问题:

    • Q1:指向 slice 的指针在堆还是栈上?
      • 指针本身在栈上,所指的实际数据一般在堆上
    • Q2:很多数据结构,如 Mutex 包含 DST,为什么它能放在栈上?

常用基本的trait

  • Debug/Default/Clone
  • Send/Sync/Unpin(一般自动实现了)
  • PartialEq/PartialOrd/Hash/Eq/Ord
  • Serialize/Deserialize (use feature flag)
  • Iterator
  • Deref<Target> / AsRef / From<T> / Into<T>

Rust中的test

  • unit test
    • 单元测试,它可以访问到所在crate的所有数据结构和API。无论是pub还是internal
  • doctest
    • 执行文档注释中的测试代码,保证注释文档代码的正确性。和unit test使用同样的运行方式:cargo test
  • integration test:见 tonic
    • 它和平常的crate没有差别,只能调用所需要测试的crate的公开API
  • fuzz:honggfuzz-rs(见 snow
  • proptest:quickcheck 或 proptest (见:cellar
  • benchmark:[benchmark] 现在还是 unstable,可以用 criterion(见:cellar
    • 主要是测试函数或者API的性能。通过短时间调用N次API来测量

Future: Leaf / Non-leaf事件

  • Leaf:一般是运行时定义的内部事件(e.g. event)/ 外部事件(e.g. io)

    • tokio::time::Sleep
    • tokio::net::TcpStream
  • non-leaf:一般用 async 定义的 future

    • async move {}
let fut = async {
  let mut stream = TcpStream::connect("localhost:8000").await?;
  stream.write(b"hello world\n").await?;
}

程序中大部分是non-leaf事件,它大多时候依赖于Future在leaf事件上执行poll操作,从而让状态流转

Rust中的Pin trait

image.png

  1. 原来左边的b内存存储的是0x1002这个地址值,这个地址的内容是左边的a
  2. 经过swap交换之后,右边的b还是存储的0x1002这个地址值,但是这个地址里面的内容已经变成了原来右边a的内容
  3. 同理原来右边b的值,经过swap之后,现在指向了右边的a
  4. 这两个从而造成了一个环

网络协议栈

image.png

Rust对L3-L7都有不错的支持

中心化服务的一般设计

image.png 一般通过将该过程分为多个layer来分别处理

Rust对网络协议的支持

image.png

  1. tonic用于支持GRPC
  2. tracing用于日志和log,功能强大,API简单

介绍tonic:Rust 下 GRPC 服务

  • tower-service
  • 基于 prost,生成 ProstCodec
  • 为你的 grpc service 定义生成 Service trait
pub trait Service<Request> {
    type Response;
    type Error;

    type Future: Future<Output = Result<Self::Response, Self::Error>>;
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
    fn call(&mut self, req: Request) -> Self::Future;
}

Rust TLS 支持

  • openssl
  • rustls (基于 ring)
  • tokio-tls-helper

Noise Protocol

  • TLS vs Noise protocol:动态协商 vs 静态协商

  • Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s:

    • I:发起者的固定公钥未加密就直接发给应答者
    • K:应答者的公钥发起者预先就知道
    • psk2:把预设的密码(Pre-Shared-Key )放在第 2 个握手包之后
    • ChaChaPoly:对称加密算法
    • BLAKE2s:哈希算法
  • 协议最少 0-RTT (x 或者 xpsk),之后就建立好加密通道,可以发送数据

接口

  • build:根据协议变量和固定私钥,初始化 HandshakeState
  • write(msg, buf): 根据当前的状态,撰写协议报文或者把用户传入的 buffer 加密
  • read(buf, msg):根据当前的状态,读取用户传入的 buffer,处理握手状态机或者把用户传入的 buffer 解密
  • into_transport_mode:将 HandshakeState 转为 TransportState
  • rekey:在传输模式下,用户可以调用 rekey 来更新密钥

宏编程

声明宏

  • macro_rules! 使用它来声明
  • #[macro_use] / #[macro_export] 使用它来导入和使用

过程宏

  • 类函数宏(function-like macros):println!(...)
  • 派生宏(derive macros):#[derive(Debug)]
  • 标记宏(attribute macros):#[tokio::main]

资料

【程序君的 Rust 培训(2)-哔哩哔哩】 b23.tv/2lwrfGP

代码链接 github.com/tyrchen/rus…

PPT链接 tyrchen.github.io/rust-traini…