Skip to content

Commit a811626

Browse files
committed
Introduce MakeMut for efficient Arc handling
1 parent b06bf1a commit a811626

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

druid/src/lens.rs

Lines changed: 82 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

@@ -123,6 +124,33 @@ pub trait LensExt<A: ?Sized, B: ?Sized>: Lens<A, B> {
123124
{
124125
self.then(Index::new(index))
125126
}
127+
128+
/// Access data in an `Arc` with copy-on-write semantics
129+
///
130+
/// The inner `lens` allows the scope that has to be checked for changes after a mutable access
131+
/// to be reduced.
132+
///
133+
/// ```
134+
/// # use druid::*; use std::sync::Arc;
135+
/// let lens = lens::Id.make_mut(lens::Id.index(2));
136+
/// let mut x = Arc::new(vec![0, 1, 2, 3]);
137+
/// let original = x.clone();
138+
/// assert_eq!(lens.get(&x), 2);
139+
/// lens.put(&mut x, 2);
140+
/// assert!(Arc::ptr_eq(&original, &x), "no-op writes don't cause a deep copy");
141+
/// lens.put(&mut x, 42);
142+
/// assert_eq!(&*x, &[0, 1, 42, 3]);
143+
/// ```
144+
fn make_mut<L, C, D>(self, lens: L) -> MakeMut<Self, L, C>
145+
where
146+
Self: Sized + Lens<A, Arc<C>>,
147+
B: Sized,
148+
C: Clone,
149+
D: Data,
150+
L: Lens<C, D>,
151+
{
152+
MakeMut::new(self, lens)
153+
}
126154
}
127155

128156
impl<A: ?Sized, B: ?Sized, T: Lens<A, B>> LensExt<A, B> for T {}
@@ -381,3 +409,57 @@ impl<A: ?Sized> Lens<A, A> for Id {
381409
f(data)
382410
}
383411
}
412+
413+
/// A `Lens` that exposes data within an `Arc` with copy-on-write semantics
414+
///
415+
/// A copy is only made in the event that a different value is written.
416+
#[derive(Debug, Copy, Clone)]
417+
pub struct MakeMut<L, M, B> {
418+
arc: L,
419+
inner: M,
420+
_marker: PhantomData<B>,
421+
}
422+
423+
impl<L, M, B> MakeMut<L, M, B> {
424+
/// Construct a lens manipulating the `inner` part of `arc`
425+
///
426+
/// See also `LensExt::make_mut`
427+
pub fn new<A, C>(arc: L, inner: M) -> Self
428+
where
429+
A: ?Sized,
430+
B: Clone,
431+
C: Data,
432+
L: Lens<A, Arc<B>>,
433+
M: Lens<B, C>,
434+
{
435+
Self {
436+
arc,
437+
inner,
438+
_marker: PhantomData,
439+
}
440+
}
441+
}
442+
443+
impl<A, B, C, L, M> Lens<A, C> for MakeMut<L, M, B>
444+
where
445+
A: ?Sized,
446+
B: Clone,
447+
C: Data,
448+
L: Lens<A, Arc<B>>,
449+
M: Lens<B, C>,
450+
{
451+
fn with<V, F: FnOnce(&C) -> V>(&self, data: &A, f: F) -> V {
452+
self.arc.with(data, |arc| self.inner.with(arc, f))
453+
}
454+
455+
fn with_mut<V, F: FnOnce(&mut C) -> V>(&self, data: &mut A, f: F) -> V {
456+
self.arc.with_mut(data, |arc| {
457+
let mut temp = self.inner.with(arc, |x| x.clone());
458+
let v = f(&mut temp);
459+
if self.inner.with(arc, |x| !x.same(&temp)) {
460+
self.inner.with_mut(Arc::make_mut(arc), |x| *x = temp);
461+
}
462+
v
463+
})
464+
}
465+
}

0 commit comments

Comments
 (0)