Skip to content

Commit

Permalink
add code for 24
Browse files Browse the repository at this point in the history
  • Loading branch information
tyrchen committed Oct 21, 2021
1 parent 29612d0 commit 9ab70c1
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 1 deletion.
15 changes: 15 additions & 0 deletions 24_advanced_trait_objects/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "advanced_trait_objects"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "trait_object"
harness = false
39 changes: 39 additions & 0 deletions 24_advanced_trait_objects/benches/trait_object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use advanced_trait_objects::{
execute_boxed_trait_object, execute_generics, execute_trait_object, Shell,
};
use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn generics_benchmark(c: &mut Criterion) {
c.bench_function("generics", |b| {
b.iter(|| {
let cmd = Shell::new("ls", &[]);
execute_generics(black_box(&cmd)).unwrap();
})
});
}

pub fn trait_object_benchmark(c: &mut Criterion) {
c.bench_function("trait object", |b| {
b.iter(|| {
let cmd = Shell::new("ls", &[]);
execute_trait_object(black_box(&cmd)).unwrap();
})
});
}

pub fn boxed_object_benchmark(c: &mut Criterion) {
c.bench_function("boxed object", |b| {
b.iter(|| {
let cmd = Box::new(Shell::new("ls", &[]));
execute_boxed_trait_object(black_box(cmd)).unwrap();
})
});
}

criterion_group!(
benches,
generics_benchmark,
trait_object_benchmark,
boxed_object_benchmark
);
criterion_main!(benches);
5 changes: 5 additions & 0 deletions 24_advanced_trait_objects/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod service;
mod trait_object_in_fn;

pub use service::*;
pub use trait_object_in_fn::*;
39 changes: 39 additions & 0 deletions 24_advanced_trait_objects/src/service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::{error::Error, sync::Arc};

// 定义类型,让 KV server 里的 trait 可以被编译通过
pub type KvError = Box<dyn Error + Send + Sync>;
pub struct Value(i32);
pub struct Kvpair(i32, i32);

/// 对存储的抽象,我们不关心数据存在哪儿,但需要定义外界如何和存储打交道
pub trait Storage: Send + Sync + 'static {
fn get(&self, table: &str, key: &str) -> Result<Option<Value>, KvError>;
fn set(&self, table: &str, key: String, value: Value) -> Result<Option<Value>, KvError>;
fn contains(&self, table: &str, key: &str) -> Result<bool, KvError>;
fn del(&self, table: &str, key: &str) -> Result<Option<Value>, KvError>;
fn get_all(&self, table: &str) -> Result<Vec<Kvpair>, KvError>;
fn get_iter(&self, table: &str) -> Result<Box<dyn Iterator<Item = Kvpair>>, KvError>;
}

// 使用 trait object,不需要泛型参数,也不需要 ServiceInner 了
pub struct Service {
pub store: Arc<dyn Storage>,
}

// impl 的代码略微简单一些
impl Service {
pub fn new<S: Storage>(store: S) -> Self {
Self {
store: Arc::new(store),
}
}
}

// 实现 trait 时也不需要带着泛型参数
impl Clone for Service {
fn clone(&self) -> Self {
Self {
store: Arc::clone(&self.store),
}
}
}
65 changes: 65 additions & 0 deletions 24_advanced_trait_objects/src/trait_object_in_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::{error::Error, process::Command};

pub type BoxedError = Box<dyn Error + Send + Sync>;

pub trait Executor {
fn run(&self) -> Result<Option<i32>, BoxedError>;
}

pub struct Shell<'a, 'b> {
cmd: &'a str,
args: &'b [&'a str],
}

impl<'a, 'b> Shell<'a, 'b> {
pub fn new(cmd: &'a str, args: &'b [&'a str]) -> Self {
Self { cmd, args }
}
}

impl<'a, 'b> Executor for Shell<'a, 'b> {
fn run(&self) -> Result<Option<i32>, BoxedError> {
let output = Command::new(self.cmd).args(self.args).output()?;
Ok(output.status.code())
}
}

/// 使用泛型参数
pub fn execute_generics(cmd: &impl Executor) -> Result<Option<i32>, BoxedError> {
cmd.run()
}

/// 使用 trait object: &dyn T
pub fn execute_trait_object(cmd: &dyn Executor) -> Result<Option<i32>, BoxedError> {
cmd.run()
}

/// 使用 trait object: Box<dyn T>
pub fn execute_boxed_trait_object(cmd: Box<dyn Executor>) -> Result<Option<i32>, BoxedError> {
cmd.run()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn shell_shall_work() {
let cmd = Shell::new("ls", &[]);
let result = cmd.run().unwrap();
assert_eq!(result, Some(0));
}

#[test]
fn execute_shall_work() {
let cmd = Shell::new("ls", &[]);

let result = execute_generics(&cmd).unwrap();
assert_eq!(result, Some(0));
let result = execute_trait_object(&cmd).unwrap();
assert_eq!(result, Some(0));
let boxed = Box::new(cmd);
let result = execute_boxed_trait_object(boxed).unwrap();
assert_eq!(result, Some(0));
}
}
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ members = [
"19_closure",
"21_kv",
"mid_term_rgrep",
"23_advanced_generics"
"23_advanced_generics",
"24_advanced_trait_objects",
]

0 comments on commit 9ab70c1

Please sign in to comment.