Skip to content

Commit 08d5197

Browse files
authored
Merge pull request #499 from petertseng/react-cell-ids
react: Distinguish cell IDs
2 parents cf2d3e4 + 169ea90 commit 08d5197

File tree

3 files changed

+186
-142
lines changed

3 files changed

+186
-142
lines changed

exercises/react/example.rs

Lines changed: 103 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
use std::collections::HashMap;
22

3-
pub type CellID = usize;
4-
pub type CallbackID = usize;
5-
6-
#[derive(Debug, PartialEq)]
7-
pub enum SetValueError {
8-
NonexistentCell,
9-
ComputeCell,
3+
/// `InputCellID` is a unique identifier for an input cell.
4+
#[derive(Clone, Copy, Debug, PartialEq)]
5+
pub struct InputCellID(usize);
6+
/// `ComputeCellID` is a unique identifier for a compute cell.
7+
/// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable,
8+
/// demonstrated by the following tests:
9+
///
10+
/// ```compile_fail
11+
/// let mut r = react::Reactor::new();
12+
/// let input: react::ComputeCellID = r.create_input(111);
13+
/// ```
14+
///
15+
/// ```compile_fail
16+
/// let mut r = react::Reactor::new();
17+
/// let input = r.create_input(111);
18+
/// let compute: react::InputCellID = r.create_compute(&[react::CellID::Input(input)], |_| 222).unwrap();
19+
/// ```
20+
#[derive(Clone, Copy, Debug, PartialEq)]
21+
pub struct ComputeCellID(usize);
22+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
23+
pub struct CallbackID(usize);
24+
25+
#[derive(Clone, Copy, Debug, PartialEq)]
26+
pub enum CellID {
27+
Input(InputCellID),
28+
Compute(ComputeCellID),
1029
}
1130

1231
#[derive(Debug, PartialEq)]
@@ -15,80 +34,97 @@ pub enum RemoveCallbackError {
1534
NonexistentCallback,
1635
}
1736

18-
struct Cell<'a, T: Copy> {
37+
struct Cell<T: Copy> {
1938
value: T,
2039
last_value: T,
21-
dependents: Vec<CellID>,
22-
cell_type: CellType<'a, T>,
23-
callbacks_issued: usize,
24-
callbacks: HashMap<CallbackID, Box<FnMut(T) -> () + 'a>>,
40+
dependents: Vec<ComputeCellID>,
2541
}
2642

27-
enum CellType<'a, T: Copy> {
28-
Input,
29-
Compute(Vec<CellID>, Box<Fn(&[T]) -> T + 'a>),
43+
struct ComputeCell<'a, T: Copy> {
44+
cell: Cell<T>,
45+
46+
dependencies: Vec<CellID>,
47+
f: Box<Fn(&[T]) -> T + 'a>,
48+
callbacks_issued: usize,
49+
callbacks: HashMap<CallbackID, Box<FnMut(T) -> () + 'a>>,
3050
}
3151

32-
impl <'a, T: Copy> Cell<'a, T> {
33-
fn new(initial: T, cell_type: CellType<'a, T>) -> Self {
52+
impl <T: Copy> Cell<T> {
53+
fn new(initial: T) -> Self {
3454
Cell {
3555
value: initial,
3656
last_value: initial,
3757
dependents: Vec::new(),
38-
cell_type: cell_type,
58+
}
59+
}
60+
}
61+
62+
impl <'a, T: Copy> ComputeCell<'a, T> {
63+
fn new<F: Fn(&[T]) -> T + 'a>(initial: T, dependencies: Vec<CellID>, f: F) -> Self {
64+
ComputeCell {
65+
cell: Cell::new(initial),
66+
67+
dependencies,
68+
f: Box::new(f),
3969
callbacks_issued: 0,
4070
callbacks: HashMap::new(),
4171
}
4272
}
4373
}
4474

4575
pub struct Reactor<'a, T: Copy> {
46-
cells: Vec<Cell<'a, T>>,
76+
inputs: Vec<Cell<T>>,
77+
computes: Vec<ComputeCell<'a, T>>,
4778
}
4879

4980
impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
5081
pub fn new() -> Self {
5182
Reactor{
52-
cells: Vec::new(),
83+
inputs: Vec::new(),
84+
computes: Vec::new(),
5385
}
5486
}
5587

56-
pub fn create_input(&mut self, initial: T) -> CellID {
57-
self.cells.push(Cell::new(initial, CellType::Input));
58-
self.cells.len() - 1
88+
pub fn create_input(&mut self, initial: T) -> InputCellID {
89+
self.inputs.push(Cell::new(initial));
90+
InputCellID(self.inputs.len() - 1)
5991
}
6092

61-
pub fn create_compute<F: Fn(&[T]) -> T + 'a>(&mut self, dependencies: &[CellID], compute_func: F) -> Result<CellID, CellID> {
93+
pub fn create_compute<F: Fn(&[T]) -> T + 'a>(&mut self, dependencies: &[CellID], compute_func: F) -> Result<ComputeCellID, CellID> {
6294
// Check all dependencies' validity before modifying any of them,
6395
// so that we don't perform an incorrect partial write.
64-
if let Some(&invalid) = dependencies.iter().find(|&dep| *dep >= self.cells.len()) {
65-
return Err(invalid);
96+
for &dep in dependencies {
97+
match dep {
98+
CellID::Input(InputCellID(id)) => if id >= self.inputs.len() { return Err(dep) },
99+
CellID::Compute(ComputeCellID(id)) => if id >= self.computes.len() { return Err(dep) },
100+
}
66101
}
67-
let new_id = self.cells.len();
68-
for &id in dependencies {
69-
self.cells.get_mut(id).unwrap().dependents.push(new_id);
102+
let new_id = ComputeCellID(self.computes.len());
103+
for &dep in dependencies {
104+
match dep {
105+
CellID::Input(InputCellID(id)) => self.inputs[id].dependents.push(new_id),
106+
CellID::Compute(ComputeCellID(id)) => self.computes[id].cell.dependents.push(new_id),
107+
}
70108
}
71109
let inputs: Vec<_> = dependencies.iter().map(|&id| self.value(id).unwrap()).collect();
72110
let initial = compute_func(&inputs);
73-
self.cells.push(Cell::new(initial, CellType::Compute(dependencies.iter().cloned().collect(), Box::new(compute_func))));
111+
self.computes.push(ComputeCell::new(initial, dependencies.to_vec(), compute_func));
74112
Ok(new_id)
75113
}
76114

77115
pub fn value(&self, id: CellID) -> Option<T> {
78-
self.cells.get(id).map(|c| c.value)
116+
match id {
117+
CellID::Input(InputCellID(id)) => self.inputs.get(id).map(|c| c.value),
118+
CellID::Compute(ComputeCellID(id)) => self.computes.get(id).map(|c| c.cell.value),
119+
}
79120
}
80121

81-
pub fn set_value(&mut self, id: CellID, new_value: T) -> Result<(), SetValueError> {
82-
match self.cells.get_mut(id) {
83-
Some(c) => match c.cell_type {
84-
CellType::Input => {
85-
c.value = new_value;
86-
Ok(c.dependents.clone())
87-
},
88-
CellType::Compute(_, _) => Err(SetValueError::ComputeCell),
89-
},
90-
None => Err(SetValueError::NonexistentCell),
91-
}.map(|deps| {
122+
pub fn set_value(&mut self, id: InputCellID, new_value: T) -> bool {
123+
let InputCellID(id) = id;
124+
self.inputs.get_mut(id).map(|c| {
125+
c.value = new_value;
126+
c.dependents.clone()
127+
}).map(|deps| {
92128
for &d in deps.iter() {
93129
self.update_dependent(d);
94130
}
@@ -97,19 +133,22 @@ impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
97133
for d in deps {
98134
self.fire_callbacks(d);
99135
}
100-
})
136+
}).is_some()
101137
}
102138

103-
pub fn add_callback<F: FnMut(T) -> () + 'a>(&mut self, id: CellID, callback: F) -> Option<CallbackID> {
104-
self.cells.get_mut(id).map(|c| {
139+
pub fn add_callback<F: FnMut(T) -> () + 'a>(&mut self, id: ComputeCellID, callback: F) -> Option<CallbackID> {
140+
let ComputeCellID(id) = id;
141+
self.computes.get_mut(id).map(|c| {
105142
c.callbacks_issued += 1;
106-
c.callbacks.insert(c.callbacks_issued, Box::new(callback));
107-
c.callbacks_issued
143+
let cbid = CallbackID(c.callbacks_issued);
144+
c.callbacks.insert(cbid, Box::new(callback));
145+
cbid
108146
})
109147
}
110148

111-
pub fn remove_callback(&mut self, cell: CellID, callback: CallbackID) -> Result<(), RemoveCallbackError> {
112-
match self.cells.get_mut(cell) {
149+
pub fn remove_callback(&mut self, cell: ComputeCellID, callback: CallbackID) -> Result<(), RemoveCallbackError> {
150+
let ComputeCellID(cell) = cell;
151+
match self.computes.get_mut(cell) {
113152
Some(c) => match c.callbacks.remove(&callback) {
114153
Some(_) => Ok(()),
115154
None => Err(RemoveCallbackError::NonexistentCallback),
@@ -118,29 +157,28 @@ impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
118157
}
119158
}
120159

121-
fn update_dependent(&mut self, id: CellID) {
160+
fn update_dependent(&mut self, id: ComputeCellID) {
161+
let ComputeCellID(id) = id;
162+
122163
let (new_value, dependents) = {
123164
// This block limits the scope of the self.cells borrow.
124165
// This is necessary becaue we borrow it mutably below.
125-
let (dependencies, f, dependents) = match self.cells.get(id) {
126-
Some(c) => match c.cell_type {
127-
CellType::Input => panic!("Input cell can't be a dependent"),
128-
CellType::Compute(ref dependencies, ref f) => (dependencies, f, c.dependents.clone()),
129-
},
166+
let (dependencies, f, dependents) = match self.computes.get(id) {
167+
Some(c) => (&c.dependencies, &c.f, c.cell.dependents.clone()),
130168
None => panic!("Cell to update disappeared while querying"),
131169
};
132170
let inputs: Vec<_> = dependencies.iter().map(|&id| self.value(id).unwrap()).collect();
133171
(f(&inputs), dependents)
134172
};
135173

136-
match self.cells.get_mut(id) {
174+
match self.computes.get_mut(id) {
137175
Some(c) => {
138-
if c.value == new_value {
176+
if c.cell.value == new_value {
139177
// No change here, we don't need to update our dependents.
140178
// (It wouldn't hurt to, but it would be unnecessary work)
141179
return;
142180
}
143-
c.value = new_value;
181+
c.cell.value = new_value;
144182
},
145183
None => panic!("Cell to update disappeared while updating"),
146184
}
@@ -150,19 +188,20 @@ impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
150188
}
151189
}
152190

153-
fn fire_callbacks(&mut self, id: CellID) {
154-
let dependents = match self.cells.get_mut(id) {
191+
fn fire_callbacks(&mut self, id: ComputeCellID) {
192+
let ComputeCellID(id) = id;
193+
let dependents = match self.computes.get_mut(id) {
155194
Some(c) => {
156-
if c.value == c.last_value {
195+
if c.cell.value == c.cell.last_value {
157196
// Value hasn't changed since last callback fire.
158197
// We thus shouldn't fire the callbacks.
159198
return
160199
}
161200
for cb in c.callbacks.values_mut() {
162-
cb(c.value);
201+
cb(c.cell.value);
163202
}
164-
c.last_value = c.value;
165-
c.dependents.clone()
203+
c.cell.last_value = c.cell.value;
204+
c.cell.dependents.clone()
166205
},
167206
None => panic!("Callback cell disappeared"),
168207
};

exercises/react/src/lib.rs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
1-
// Because these are passed without & to some functions,
2-
// it will probably be necessary for these two types to be Copy.
3-
pub type CellID = ();
4-
pub type CallbackID = ();
1+
/// `InputCellID` is a unique identifier for an input cell.
2+
#[derive(Clone, Copy, Debug, PartialEq)]
3+
pub struct InputCellID();
4+
/// `ComputeCellID` is a unique identifier for a compute cell.
5+
/// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable,
6+
/// demonstrated by the following tests:
7+
///
8+
/// ```compile_fail
9+
/// let mut r = react::Reactor::new();
10+
/// let input: react::ComputeCellID = r.create_input(111);
11+
/// ```
12+
///
13+
/// ```compile_fail
14+
/// let mut r = react::Reactor::new();
15+
/// let input = r.create_input(111);
16+
/// let compute: react::InputCellID = r.create_compute(&[react::CellID::Input(input)], |_| 222).unwrap();
17+
/// ```
18+
#[derive(Clone, Copy, Debug, PartialEq)]
19+
pub struct ComputeCellID();
20+
#[derive(Clone, Copy, Debug, PartialEq)]
21+
pub struct CallbackID();
522

6-
#[derive(Debug, PartialEq)]
7-
pub enum SetValueError {
8-
NonexistentCell,
9-
ComputeCell,
23+
#[derive(Clone, Copy, Debug, PartialEq)]
24+
pub enum CellID {
25+
Input(InputCellID),
26+
Compute(ComputeCellID),
1027
}
1128

1229
#[derive(Debug, PartialEq)]
@@ -28,7 +45,7 @@ impl <T: Copy + PartialEq> Reactor<T> {
2845
}
2946

3047
// Creates an input cell with the specified initial value, returning its ID.
31-
pub fn create_input(&mut self, _initial: T) -> CellID {
48+
pub fn create_input(&mut self, _initial: T) -> InputCellID {
3249
unimplemented!()
3350
}
3451

@@ -45,7 +62,7 @@ impl <T: Copy + PartialEq> Reactor<T> {
4562
// Notice that there is no way to *remove* a cell.
4663
// This means that you may assume, without checking, that if the dependencies exist at creation
4764
// time they will continue to exist as long as the Reactor exists.
48-
pub fn create_compute<F: Fn(&[T]) -> T>(&mut self, _dependencies: &[CellID], _compute_func: F) -> Result<CellID, CellID> {
65+
pub fn create_compute<F: Fn(&[T]) -> T>(&mut self, _dependencies: &[CellID], _compute_func: F) -> Result<ComputeCellID, CellID> {
4966
unimplemented!()
5067
}
5168

@@ -62,16 +79,13 @@ impl <T: Copy + PartialEq> Reactor<T> {
6279

6380
// Sets the value of the specified input cell.
6481
//
65-
// Returns an Err if either:
66-
// * the cell does not exist
67-
// * the specified cell is a compute cell, since compute cells cannot have their values
68-
// directly set.
82+
// Returns false if the cell does not exist.
6983
//
7084
// Similarly, you may wonder about `get_mut(&mut self, id: CellID) -> Option<&mut Cell>`, with
7185
// a `set_value(&mut self, new_value: T)` method on `Cell`.
7286
//
7387
// As before, that turned out to add too much extra complexity.
74-
pub fn set_value(&mut self, _id: CellID, _new_value: T) -> Result<(), SetValueError> {
88+
pub fn set_value(&mut self, _id: InputCellID, _new_value: T) -> bool {
7589
unimplemented!()
7690
}
7791

@@ -87,7 +101,7 @@ impl <T: Copy + PartialEq> Reactor<T> {
87101
// * Exactly once if the compute cell's value changed as a result of the set_value call.
88102
// The value passed to the callback should be the final value of the compute cell after the
89103
// set_value call.
90-
pub fn add_callback<F: FnMut(T) -> ()>(&mut self, _id: CellID, _callback: F) -> Option<CallbackID> {
104+
pub fn add_callback<F: FnMut(T) -> ()>(&mut self, _id: ComputeCellID, _callback: F) -> Option<CallbackID> {
91105
unimplemented!()
92106
}
93107

@@ -96,7 +110,7 @@ impl <T: Copy + PartialEq> Reactor<T> {
96110
// Returns an Err if either the cell or callback does not exist.
97111
//
98112
// A removed callback should no longer be called.
99-
pub fn remove_callback(&mut self, cell: CellID, callback: CallbackID) -> Result<(), RemoveCallbackError> {
113+
pub fn remove_callback(&mut self, cell: ComputeCellID, callback: CallbackID) -> Result<(), RemoveCallbackError> {
100114
unimplemented!(
101115
"Remove the callback identified by the CallbackID {:?} from the cell {:?}",
102116
callback,

0 commit comments

Comments
 (0)