Skip to content

Commit 6bb1c41

Browse files
committed
Introduce MakeMut for efficient Arc handling
1 parent 1d62cc5 commit 6bb1c41

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

druid/src/lens.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
use std::marker::PhantomData;
1818
use std::ops;
19+
use std::sync::Arc;
1920

2021
pub use druid_derive::Lens;
2122

@@ -312,3 +313,57 @@ impl<A: ?Sized> Lens<A, A> for Id {
312313
f(data)
313314
}
314315
}
316+
317+
/// A `Lens` that exposes data within an `Arc` with copy-on-write semantics
318+
///
319+
/// A copy is only made in the event that a different value is written.
320+
#[derive(Debug, Copy, Clone)]
321+
pub struct MakeMut<L, M, B> {
322+
arc: L,
323+
inner: M,
324+
_marker: PhantomData<B>,
325+
}
326+
327+
impl<L, M, B> MakeMut<L, M, B> {
328+
/// Construct a lens manipulating the `inner` part of `arc`
329+
///
330+
/// See also `LensExt::make_mut`
331+
pub fn new<A, C>(arc: L, inner: M) -> Self
332+
where
333+
A: ?Sized,
334+
B: Clone,
335+
C: Data,
336+
L: Lens<A, Arc<B>>,
337+
M: Lens<B, C>,
338+
{
339+
Self {
340+
arc,
341+
inner,
342+
_marker: PhantomData,
343+
}
344+
}
345+
}
346+
347+
impl<A, B, C, L, M> Lens<A, C> for MakeMut<L, M, B>
348+
where
349+
A: ?Sized,
350+
B: Clone,
351+
C: Data,
352+
L: Lens<A, Arc<B>>,
353+
M: Lens<B, C>,
354+
{
355+
fn with<V, F: FnOnce(&C) -> V>(&self, data: &A, f: F) -> V {
356+
self.arc.with(data, |arc| self.inner.with(arc, f))
357+
}
358+
359+
fn with_mut<V, F: FnOnce(&mut C) -> V>(&self, data: &mut A, f: F) -> V {
360+
self.arc.with_mut(data, |arc| {
361+
let mut temp = self.inner.with(arc, |x| x.clone());
362+
let v = f(&mut temp);
363+
if self.inner.with(arc, |x| !x.same(&temp)) {
364+
self.inner.with_mut(&mut Arc::make_mut(arc), |x| *x = temp);
365+
}
366+
v
367+
})
368+
}
369+
}

druid/src/lens_ext.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::ops;
2+
use std::sync::Arc;
23

3-
use crate::lens::{Deref, Index, Then};
4+
use crate::data::Data;
5+
use crate::lens::{Deref, Index, MakeMut, Then};
46
use crate::Lens;
57

68
/// Helpers for manipulating `Lens`es
@@ -68,6 +70,33 @@ pub trait LensExt<A: ?Sized, B: ?Sized>: Lens<A, B> {
6870
{
6971
self.then(Index::new(index))
7072
}
73+
74+
/// Access data in an `Arc` with copy-on-write semantics
75+
///
76+
/// The inner `lens` allows the scope that has to be checked for changes after a mutable access
77+
/// to be reduced.
78+
///
79+
/// ```
80+
/// # use druid::*; use std::sync::Arc;
81+
/// let lens = lens::Id.make_mut(lens::Id.index(2));
82+
/// let mut x = Arc::new(vec![0, 1, 2, 3]);
83+
/// let original = x.clone();
84+
/// assert_eq!(lens.get(&x), 2);
85+
/// lens.set(&mut x, 2);
86+
/// assert!(Arc::ptr_eq(&original, &x), "no-op writes don't force a clone");
87+
/// lens.set(&mut x, 42);
88+
/// assert_eq!(&*x, &[0, 1, 42, 3]);
89+
/// ```
90+
fn make_mut<L, C, D>(self, lens: L) -> MakeMut<Self, L, C>
91+
where
92+
Self: Sized + Lens<A, Arc<C>>,
93+
B: Sized,
94+
C: Clone,
95+
D: Data,
96+
L: Lens<C, D>,
97+
{
98+
MakeMut::new(self, lens)
99+
}
71100
}
72101

73102
impl<A: ?Sized, B: ?Sized, T: Lens<A, B>> LensExt<A, B> for T {}

0 commit comments

Comments
 (0)