-
Notifications
You must be signed in to change notification settings - Fork 359
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
165 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters