陈天 · Rust 编程第一课
陈天
Tubi TV 研发副总裁
23195 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 65 讲
基础篇 (21讲)
陈天 · Rust 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

26|阶段实操(3):构建一个简单的KV server-高级trait技巧

实现 Iterator trait
实现 From trait
数据转换
使用 IntoIterator trait
无法编译通过的问题
使用 clone() 获取 table 的 snapshot
测试
实现 Storage trait
Notify 和 NotifyMut trait
封装 StorageIter
处理 Iterator
SledDb
事件通知机制
构造者模式(Builder Pattern)
事件处理函数注册
get_iter() 方法
为 RocksDB 实现 Storage trait
修改 Notify 和 NotifyMut trait
对照已完成的 KV server 其它的 6 个命令
深入了解 Rust 的基础知识
Rust 编译器的简化
为带泛型参数的数据结构实现 trait
trait 的威力
事件通知
使用 SledDb
为持久化数据库实现 Storage trait
支持事件通知
Storage trait
思考题
小结
构建新的 KV server
构建一个简单的KV server
缺乏的是设计和架构的能力
需要足够多的代码阅读和撰写的历练
函数作为类比
泛型的抽象层级
使用和设计理念
基础知识
阶段实操(3)
泛型
Rust学习总结

该思维导图由 AI 生成,仅供参考

你好,我是陈天。
到现在,泛型的基础知识、具体如何使用以及设计理念,我们已经学得差不多了,也和函数作了类比帮助你理解,泛型就是数据结构的函数。
如果你觉得泛型难学,是因为它的抽象层级比较高,需要足够多的代码阅读和撰写的历练。所以,通过学习,现阶段你能够看懂包含泛型的代码就够了,至于使用,只能靠你自己在后续练习中不断体会总结。如果实在觉得不好懂,某种程度上说,你缺乏的不是泛型的能力,而是设计和架构的能力
今天我们就用之前 1.0 版简易的 KV store 来历练一把,看看怎么把之前学到的知识融入代码中。
21 讲22 讲中,我们已经完成了 KV store 的基本功能,但留了两个小尾巴:
Storage trait 的 get_iter() 方法没有实现;
Service 的 execute() 方法里面还有一些 TODO,需要处理事件的通知。
我们一个个来解决。先看 get_iter() 方法。

处理 Iterator

在开始撰写代码之前,先把之前在 src/storage/mod.rs 里注掉的测试,加回来:
#[test]
fn memtable_iter_should_work() {
let store = MemTable::new();
test_get_iter(store);
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了如何构建一个简单的KV server,并重点讲解了高级trait技巧。作者通过实例讲解了如何处理Iterator,并介绍了Rust标准库中的IntoIterator trait。在实现get_iter()方法时,作者使用了IntoIterator trait将数据结构的所有权转移到Iterator中,并对Iterator中的数据进行转换。随后,作者提出了对get_iter()方法的封装,使得Storage trait的实现者只需要提供拥有所有权的Iterator,并对Iterator中的Item类型提供Into<Kvpair>。最后,作者通过构建一个StorageIter并实现Iterator trait来简化get_iter()方法的实现。文章还介绍了如何支持事件通知,通过注册事件处理函数和实现事件通知机制,使得注册的事件处理函数可以得到执行。整体来说,本文内容涵盖了Rust编程中的高级trait技巧和事件通知机制的实现,对于想要深入学习Rust编程的读者具有很高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《陈天 · Rust 编程第一课》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(8)

  • 最新
  • 精选
  • losuika
    rocksdb 的实现 https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1f4b28ff3fcbdb421a0743d47c7b75c3,rocksdb 需要开启 feature multi-threaded-cf ,然后感觉 trait 约束是不是也要改下?需要带上生命周期,rocksdb 库似乎没有办法拿到所有权的迭代器

    作者回复: 嗯,确实如此。非常棒!

    2021-12-19
    3
    3
  • losuika
    Option 有个 transpose ,可以直接 Option<Result> 到 Result<Option>

    作者回复: 对

    2021-12-19
    1
  • 罗杰
    哈 昨天终于完成 21,22,今天继续

    作者回复: 👍

    2021-10-27
    1
  • Geek_e2201d
    陈老师,我尝试把MemTable与SledDb整合到一个Server中,通过config参数来动态选择MemTable还是SledDb。尝试了很久都失败了,编译器提示错误为: 50 | let resp = service_cloned.execute(msg); | ^^^^^^^ method cannot be called on `Service<Box<dyn Storage>>` due to unsatisfied trait bounds ... = note: the following trait bounds were not satisfied: `Box<dyn Storage>: Storage` 我的疑问是Box<dyn Storage>为什么不满足Storage呢? server.rs代码如下: use anyhow::Result; use async_prost::AsyncProstStream; use futures::prelude::*; use kv::{CommandRequest, CommandResponse, MemTable, SledDb, Service, Storage}; use tokio::net::TcpListener; use kv::service::ServiceInner; use tracing::info; use clap::{Parser, ArgEnum}; #[derive(Parser)] #[clap(name = "kv server")] struct Cli { ​#[clap(arg_enum, long, short, default_value="memory")] ​storage: StorageType, ​#[clap(long, short='d', default_value="./sled")] ​sled_dir: String, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)] enum StorageType { ​Memory, ​Sled, } #[tokio::main] async fn main() -> Result<()> { ​tracing_subscriber::fmt::init(); ​let args: Cli = Cli::parse(); ​let addr = "127.0.0.1:9527"; ​let listener = TcpListener::bind(addr).await?; ​info!("Start listening on {}", addr); ​let db : Box<dyn Storage> = match args.storage { ​StorageType::Memory => Box::new(MemTable::default()), ​StorageType::Sled => Box::new(SledDb::new(args.sled_dir)), ​}; ​let service: Service<Box<dyn Storage>> = ServiceInner::new(db).into(); ​loop { ​let (stream, addr) = listener.accept().await?; ​info!("Client {:?} connected", addr); ​let service_cloned = service.clone(); ​tokio::spawn(async move { ​let mut stream = ​AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); ​while let Some(Ok(msg)) = stream.next().await { ​info!("Got a new command: {:?}", msg); ​let resp = service_cloned.execute(msg); ​info!("Response: {:?}", resp); ​stream.send(resp).await.unwrap(); ​} ​info!("Client {:?} disconnected", addr); ​}); ​}

    作者回复: 如果你使用 trait object,那么你的 service 定义就不需要用 Store trait bound 了,可以这样: pub struct Service { inner: Arc<ServiceInner>, broadcaster: Arc<Broadcaster>, } pub struct ServiceInner { store: Box<dyn Store>, on_received: Vec<fn(&CommandRequest)>, on_executed: Vec<fn(&CommandResponse)>, on_before_send: Vec<fn(&mut CommandResponse)>, on_after_send: Vec<fn()>, }

    2022-01-14
    2
  • 无常
    请问老师,设计和架构应该如何学习,有什么推荐的资料吗?

    作者回复: 可以看看 Fundamentals of software architecture

    2021-12-29
  • dva
    第二题我想到的是,不过这样要改注册函数,看起来怪怪的,不知道老师的方法是什么 pub trait Notify1<Arg,E>{ fn notify(&self,arg:&Arg)->Option<E>; } impl <Arg,E>Notify1<Arg,E>for Vec<fn(&Arg)->Option<E>> { fn notify(&self, arg: &Arg) -> Option<E> { for f in self{ match f(arg){ Some(e)=> return Some(e), _=>{}, } } None } } 第三题和老师写的sleddb差不多,直接加了一些TryFrom,中间遇到没有装llvm clang的错误 “error: failed to run custom build command for `librocksdb-sys ...” 还有起名字 rocksdb和包名冲突。写这个扩展有个非常深的体会就是,编译器提示真好。

    作者回复: 👍 第二题可以看 github repo 下这章的代码

    2021-11-26
    2
  • bestgopher
    我们服务是异步的,但是sled读取文件没看到异步操作,这样是否有问题呢?
    2022-06-11
  • Alvin
    老师 跟着课程的代码实现过程发现最后持久化存储这边会报个错 the trait `From<&[u8]>` is not implemented for `abi::Value`
    2022-04-16
    4
收起评论
显示
设置
留言
8
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部