-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.rs
146 lines (125 loc) · 3.89 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use std::any::{TypeId, Any};
use std::collections::HashMap;
// These Data are custom structs to distinguash the same inner types.
#[derive(Debug)]
struct DataA(char);
#[derive(Debug)]
struct DataB(char);
// Concrete data storage.
// Assume that this is a storage that keeps your heterogeneous data.
// It's super simple, but for practical usage, we should make this more flexible and safe.
struct DataStorage {
data: HashMap<TypeId, Box<dyn Any>>,
}
impl DataStorage {
// Makes sample data.
fn new() -> Self {
let mut data: HashMap<TypeId, Box<dyn Any>> = HashMap::new();
data.insert(TypeId::of::<DataA>(), Box::new(vec![DataA('a'), DataA('b')]));
data.insert(TypeId::of::<DataB>(), Box::new(vec![DataB('c'), DataB('d')]));
Self { data }
}
}
trait Store {
fn as_slice<T: 'static>(&self) -> &[T];
fn as_mut_slice<T: 'static>(&mut self) -> &mut [T];
}
impl Store for DataStorage {
fn as_slice<T: 'static>(&self) -> &[T] {
self.data
.get(&TypeId::of::<T>())
.unwrap()
.downcast_ref::<Vec<T>>()
.unwrap()
.as_slice()
}
fn as_mut_slice<T: 'static>(&mut self) -> &mut [T] {
self.data
.get_mut(&TypeId::of::<T>())
.unwrap()
.downcast_mut::<Vec<T>>()
.unwrap()
.as_mut_slice()
}
}
trait Invokable {
fn invoke(&mut self, data: &mut DataStorage); // Depends on DataPool for object safety.
}
impl<'a, T: Runnable<'a>> Invokable for T {
#[inline]
fn invoke(&mut self, data: &mut DataStorage) {
self.run(
<T::Ref as Visit>::visit(data),
<T::Mut as VisitMut>::visit_mut(data)
);
}
}
trait Visit {
fn visit(data: &impl Store) -> Self;
}
trait VisitMut {
fn visit_mut(data: &mut impl Store) -> Self;
}
trait Runnable<'a> {
type Ref: Visit;
type Mut: VisitMut;
fn run(&mut self, r: Self::Ref, m: Self::Mut);
}
// Please implement `Visit` and `VisitMut` for more tuples. (It's ordinary in Rust for now)
// And be careful!
// Compiler infers that lifetime of `data` is different with the `Self` because we casted to raw pointers.
// This helps we to use `data` after calling this function so that we can call `visit_mut`.
// But it's dangerous, so that we need to check borrow rule manually.
impl<A: 'static, B: 'static> Visit for (&[A], &[B]) {
#[inline]
fn visit(data: &impl Store) -> Self {
unsafe {
(
&*(data.as_slice::<A>() as *const [A]),
&*(data.as_slice::<B>() as *const [B]),
)
}
}
}
impl<A: 'static, B: 'static> VisitMut for (&mut [A], &mut [B]) {
#[inline]
fn visit_mut(data: &mut impl Store) -> Self {
unsafe {
(
&mut *(data.as_mut_slice::<A>() as *mut [A]),
&mut *(data.as_mut_slice::<B>() as *mut [B]),
)
}
}
}
struct RunA;
impl<'a> Runnable<'a> for RunA {
type Ref = (&'a [DataA], &'a [DataB]);
type Mut = (&'a mut [DataA], &'a mut [DataB]);
// Data race occurs here on purpose.
fn run(&mut self, r: Self::Ref, m: Self::Mut) {
println!("RunA");
println!("r: {:?}", r);
println!("m: {:?}", m);
}
}
struct RunB;
impl<'a> Runnable<'a> for RunB {
type Ref = (&'a [DataA], &'a [DataB]);
type Mut = (&'a mut [DataA], &'a mut [DataB]);
// Data race occurs here on purpose.
fn run(&mut self, r: Self::Ref, m: Self::Mut) {
println!("RunB");
println!("r: {:?}", r);
println!("m: {:?}", m);
}
}
fn main() {
let mut data = DataStorage::new();
// We can have a list including heterogeneous functions using object safe trait `Invokable`.
let list: Vec<Box<dyn Invokable>> = vec![Box::new(RunA), Box::new(RunB)];
// Let's invoke each function.
for mut item in list {
item.invoke(&mut data);
}
}