Skip to content

Commit

Permalink
Add 'im' feature for supporting the im crate collections
Browse files Browse the repository at this point in the history
This is an experiment; the idea is to encourage the use of these
types over std::collections.

There's currently an issue or two with im::Vector, so this includes
a poor implementation of `Data` there, for the time being.
  • Loading branch information
cmyr committed May 13, 2020
1 parent 0d56631 commit eedd8af
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 5 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path=druid/Cargo.toml --all-targets --features=svg,image -- -D warnings
args: --manifest-path=druid/Cargo.toml --all-targets --features=svg,image,im -- -D warnings

- name: cargo clippy druid-derive
uses: actions-rs/cargo@v1
Expand All @@ -91,7 +91,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path=druid/Cargo.toml --features=svg,image
args: --manifest-path=druid/Cargo.toml --features=svg,image,im

- name: cargo test druid-derive
uses: actions-rs/cargo@v1
Expand Down Expand Up @@ -176,7 +176,7 @@ jobs:
with:
command: clippy
# TODO: Add svg feature when it's no longer broken with wasm
args: --manifest-path=druid/Cargo.toml --all-targets --features=image --target wasm32-unknown-unknown -- -D warnings
args: --manifest-path=druid/Cargo.toml --all-targets --features=image,im --target wasm32-unknown-unknown -- -D warnings

- name: cargo clippy druid-derive
uses: actions-rs/cargo@v1
Expand All @@ -203,7 +203,7 @@ jobs:
with:
command: test
# TODO: Add svg feature when it's no longer broken with wasm
args: --manifest-path=druid/Cargo.toml --features=image --no-run --target wasm32-unknown-unknown
args: --manifest-path=druid/Cargo.toml --features=image,im --no-run --target wasm32-unknown-unknown

- name: cargo test compile druid-derive
uses: actions-rs/cargo@v1
Expand Down Expand Up @@ -264,7 +264,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path=druid/Cargo.toml --features=svg,image
args: --manifest-path=druid/Cargo.toml --features=svg,image,im

- name: cargo test druid-derive
uses: actions-rs/cargo@v1
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ While some features like the clipboard, menus or file dialogs are not yet availa
- `UpdateCtx::request_timer` and `UpdateCtx::request_anim_frame`. ([#898] by [@finnerale])
- `UpdateCtx::size` and `LifeCycleCtx::size`. ([#917] by [@jneem])
- `WidgetExt::debug_widget_id`, for displaying widget ids on hover. ([#876] by [@cmyr])
- `im` feature, with `Data` support for the [`im` crate](https://docs.rs/im/) collections. ([#924])

### Changed

Expand Down Expand Up @@ -165,6 +166,7 @@ While some features like the clipboard, menus or file dialogs are not yet availa
[#909]: https://github.com/xi-editor/druid/pull/909
[#917]: https://github.com/xi-editor/druid/pull/917
[#920]: https://github.com/xi-editor/druid/pull/920
[#924]: https://github.com/xi-editor/druid/pull/924

## [0.5.0] - 2020-04-01

Expand Down
55 changes: 55 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions docs/src/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,17 @@ Here is an example of using `Data` to implement a simple data model.
```rust
{{#include ../book_examples/src/data_md.rs:derive}}
```

#### Collections

`Data` is expected to be cheap to clone and cheap to compare, which can cause
issues with collection types. For this reason, `Data` is not implemented for
`std` types like `Vec` or `HashMap`. This is not a huge issue, however; you can
always put these types inside an `Rc` or an `Arc`, or if you're dealing with
larger collections you can build druid with the `im` feature, which brings in
the [`im crate`], and adds a `Data` impl for the collections there. The [`im`
crate] is a collection of immutable data structures that act a lot like the `std`
collections, but can be cloned efficiently.


[`im` crate]: https://docs.rs/im
1 change: 1 addition & 0 deletions druid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fnv = "1.0.3"
xi-unicode = "0.2.0"
image = {version = "0.23.2", optional = true}
instant = { version = "0.1", features = [ "wasm-bindgen" ] }
im = { version = "14.0", optional = true }

[dependencies.simple_logger]
version = "1.6.0"
Expand Down
76 changes: 76 additions & 0 deletions druid/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ pub use druid_derive::Data;
/// This function must have a signature in the form, `fn<T>(&T, &T) -> bool`,
/// where `T` is the type of the field.
///
/// ## Collection types
///
/// `Data` is not implemented for `std` collection types, because comparing them
/// can be expensive. To use collection types with druid, there are two easy options:
/// either wrap the collection in an `Arc`, or build druid with the `im` feature,
/// which adds `Data` implementations to the collections from the [`im` crate],
/// a set of immutable data structures that fit nicely with druid.
///
/// If the `im` feature is used, the `im` crate is reexported from the root
/// of the druid crate.
///
/// ### Example:
///
/// ```
Expand All @@ -90,6 +101,7 @@ pub use druid_derive::Data;
/// checks for equality. Therefore, such types must also implement `PartialEq`.
///
/// [`Data::same`]: trait.Data.html#tymethod.same
/// [`im` crate]: https://docs.rs/im
pub trait Data: Clone + 'static {
//// ANCHOR: same_fn
/// Determine whether two values are the same.
Expand Down Expand Up @@ -375,6 +387,58 @@ impl Data for piet::Color {
}
}

//FIXME Vector::ptr_eq is not currently reliable; this is a temporary impl?
#[cfg(feature = "im")]
impl<T: Data> Data for im::Vector<T> {
fn same(&self, other: &Self) -> bool {
// for reasons outlined in https://github.com/bodil/im-rs/issues/129,
// ptr_eq always returns false for small collections. This heuristic
// falls back to using equality for collections below some threshold.
// There may be a possibility of this returning false negatives, but
// not false positives; that's an acceptable outcome.

/* this is the impl I expected to use
const INLINE_LEN: usize = 48; // bytes available before first allocation;
let inline_capacity: usize = INLINE_LEN / std::mem::size_of::<T>();
if self.len() <= inline_capacity {
self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
} else {
self.ptr_eq(other)
}
*/

self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
}
}

#[cfg(feature = "im")]
impl<K: Clone + 'static, V: Data> Data for im::HashMap<K, V> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

#[cfg(feature = "im")]
impl<T: Data> Data for im::HashSet<T> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

#[cfg(feature = "im")]
impl<K: Clone + 'static, V: Data> Data for im::OrdMap<K, V> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

#[cfg(feature = "im")]
impl<T: Data> Data for im::OrdSet<T> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

macro_rules! impl_data_for_array {
() => {};
($this:tt $($rest:tt)*) => {
Expand Down Expand Up @@ -406,4 +470,16 @@ mod test {
assert!(input.same(&[1u8, 0, 0, 1, 0]));
assert!(!input.same(&[1u8, 1, 0, 1, 0]));
}

#[test]
#[cfg(feature = "im")]
fn im_data() {
for len in 8..256 {
let input = std::iter::repeat(0_u8).take(len).collect::<im::Vector<_>>();
let mut inp2 = input.clone();
assert!(input.same(&inp2));
inp2.set(len - 1, 98);
assert!(!input.same(&inp2));
}
}
}
5 changes: 5 additions & 0 deletions druid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ use druid_shell as shell;
#[doc(inline)]
pub use druid_shell::{kurbo, piet};

// the im crate provides immutable data structures that play well with druid
#[cfg(feature = "im")]
#[doc(inline)]
pub use im;

mod app;
mod app_delegate;
mod bloom;
Expand Down

0 comments on commit eedd8af

Please sign in to comment.