no_std support
authorChris Morgan <me@chrismorgan.info>
committerChris Morgan <me@chrismorgan.info>
I’m quite pleased with how this has turned out.

Given the stability-despite-instability of hashbrown (that the API
surface we’re depending on hasn’t changed since 0.1.1), and the
deliberate altered SemVer guarantees for it, it was very tempting
to leave the hashbrown range open, `version = ">=0.1.1"` or at least
`version = ">=0.1.1, <1"`, but for some reason or other I ended up
deciding not to. I’m still of two minds about it, really.
.travis.yml
CHANGELOG.md
Cargo.toml
README.md
src/any.rs
src/lib.rs
src/raw.rs
test
test-oldest-Cargo.lock [new file with mode: 0644]

index d6e378bb8c2d619ad0041f088cee4961e5e700f8..b5c4583272b6a3db8e2526a6234b019e37fda609 100644 (file)
@@ -1,9 +1,12 @@
 language: rust
 rust:
-  - 1.34.0
+  - 1.36.0
   - nightly
   - stable
 script:
   - if [[ "$(rustc --version)" =~ -(dev|nightly) ]]; then cargo bench; fi
+  - if [[ "$(rustc --version)" =~ 1.36.0 ]]; then cp test-oldest-Cargo.lock Cargo.lock; fi
   - cargo test
   - cargo test --release
+  - cargo test --no-default-features --features hashbrown
+  - cargo test --release --no-default-features --features hashbrown
index 6477f99cd2a5cd31fd73e34cd7ae77fa411dd1a7..214ba6b0052e19550717a5bb0c602089d91bf3f1 100644 (file)
 
 - Relicensed from MIT/Apache-2.0 to BlueOak-1.0.0/MIT/Apache-2.0.
 
-- Increased the minimum supported version of Rust from 1.7.0 to 1.34.0.
+- Increased the minimum supported version of Rust from 1.7.0 to 1.36.0.
+
+- no_std is now possible in the usual way (default Cargo feature 'std'),
+  depending on alloc and hashbrown.
 
 - Removed the `bench` Cargo feature which was mostly to work around historical
   Cargo limitations, but was solved by moving benchmarks from `src/lib.rs` to
index c1ca92c2396c086acaad40e969a7ca2ca4fb6faa..011cbe2364c7f224fe9c7d7d0e62d25d45f25f8a 100644 (file)
@@ -3,10 +3,18 @@ name = "anymap"
 version = "0.12.1"
 authors = ["Chris Morgan <rust@chrismorgan.info>"]
 edition = "2018"
-rust-version = "1.34"
+rust-version = "1.36"
 description = "A safe and convenient store for one value of each type"
 repository = "https://github.com/chris-morgan/anymap"
 keywords = ["container", "any", "map"]
-categories = ["rust-patterns", "data-structures"]
+categories = ["rust-patterns", "data-structures", "no-std"]
 license = "BlueOak-1.0.0 OR MIT OR Apache-2.0"
-exclude = ["test", "benches", ".travis.yml", ".gitignore"]
+include = ["/README.md", "/COPYING", "/CHANGELOG.md", "/src"]
+
+[features]
+default = ["std"]
+std = []
+
+[dependencies]
+# The hashbrown feature, disabled by default, is exposed under different stability guarantees than the usual SemVer ones: by preference the version range will only be extended, but it may be shrunk in a MINOR release. See README.md.
+hashbrown = { version = ">=0.1.1, <0.13", optional = true }
index ea539459e4f49b6dc54543d445ac5db157c378ae..59b1d961d32766e997569e3425a9b790c2ce855c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -10,6 +10,26 @@ The ``AnyMap`` type is a friendly wrapper around a ``HashMap<TypeId, Box<dyn Any
 
 What this means is that in an ``AnyMap`` you may store zero or one values for every type.
 
+## Cargo features/dependencies/usage
+
+Typical Cargo.toml usage:
+
+```toml
+[dependencies]
+anymap = "1.0.0-beta.1"
+```
+
+No-std usage, using `alloc` and the [hashbrown](https://rust-lang.github.io/hashbrown) crate instead of `std::collections::HashMap`:
+
+```toml
+[dependencies]
+anymap = { version = "1.0.0-beta.1", default-features = false, features = ["hashbrown"] }
+```
+
+The `std` feature is enabled by default. The `hashbrown` feature overrides it. At least one of the two must be enabled.
+
+**On stability:** hashbrown is still pre-1.0.0 and experiencing breaking changes. Because it’s useful for a small fraction of users, I am retaining it, but with *different compatibility guarantees to the typical SemVer ones*. Where possible, I will just widen the range for new releases of hashbrown (e.g. change `0.12` to `>=0.12, <0.14` when they release 0.13.0), but if an incompatible change occurs, I may drop support for older versions of hashbrown with a bump to the *minor* part of the anymap version number (e.g. 1.1.0, 1.2.0). Iff you’re using this feature, this is cause to *consider* using a tilde requirement like `"~1.0"` (or spell it out as `>=1, <1.1`).
+
 ## Unsafe code in this library
 
 This library uses a fair bit of unsafe code for several reasons:
index 437eef29657e9a7bcc6687435139ce7c17cea408..15b535b343433fb5ce83cc97e15d2f9838406833 100644 (file)
@@ -1,5 +1,7 @@
 use core::fmt;
 use core::any::Any;
+#[cfg(not(feature = "std"))]
+use alloc::boxed::Box;
 
 #[doc(hidden)]
 pub trait CloneToAny {
index 966aa5a6a2312400cb4f6cf6cf71df416aba230e..6366c70b5a3781fda251734893cf2585d7f196bc 100644 (file)
@@ -4,9 +4,20 @@
 
 #![warn(missing_docs, unused_results)]
 
+#![cfg_attr(not(feature = "std"), no_std)]
+
 use core::any::{Any, TypeId};
 use core::marker::PhantomData;
 
+#[cfg(not(any(feature = "std", feature = "hashbrown")))]
+compile_error!("anymap: you must enable the 'std' feature or the 'hashbrown' feature");
+
+#[cfg(not(feature = "std"))]
+extern crate alloc;
+
+#[cfg(not(feature = "std"))]
+use alloc::boxed::Box;
+
 use raw::RawMap;
 use any::{UncheckedAnyExt, IntoBox};
 pub use any::CloneAny;
index 2b490d81583f03f6ae268dab1aaa8b67f9f3718f..081edd4bd2845400f1f3ad00ea23e9d33167362c 100644 (file)
@@ -4,7 +4,12 @@
 
 use core::any::{Any, TypeId};
 use core::borrow::Borrow;
+#[cfg(all(feature = "std", not(feature = "hashbrown")))]
 use std::collections::hash_map::{self, HashMap};
+#[cfg(feature = "hashbrown")]
+use hashbrown::hash_map::{self, HashMap};
+#[cfg(not(feature = "std"))]
+use alloc::boxed::Box;
 use core::convert::TryInto;
 use core::hash::Hash;
 use core::hash::{Hasher, BuildHasherDefault};
@@ -35,6 +40,8 @@ impl Hasher for TypeIdHasher {
 
 #[test]
 fn type_id_hasher() {
+    #[cfg(not(feature = "std"))]
+    use alloc::vec::Vec;
     fn verify_hashing_with(type_id: TypeId) {
         let mut hasher = TypeIdHasher::default();
         type_id.hash(&mut hasher);
@@ -260,12 +267,18 @@ impl<A: ?Sized + UncheckedAnyExt> IntoIterator for RawMap<A> {
 
 /// A view into a single occupied location in a `RawMap`.
 pub struct OccupiedEntry<'a, A: ?Sized + UncheckedAnyExt> {
+    #[cfg(all(feature = "std", not(feature = "hashbrown")))]
     inner: hash_map::OccupiedEntry<'a, TypeId, Box<A>>,
+    #[cfg(feature = "hashbrown")]
+    inner: hash_map::OccupiedEntry<'a, TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>,
 }
 
 /// A view into a single empty location in a `RawMap`.
 pub struct VacantEntry<'a, A: ?Sized + UncheckedAnyExt> {
+    #[cfg(all(feature = "std", not(feature = "hashbrown")))]
     inner: hash_map::VacantEntry<'a, TypeId, Box<A>>,
+    #[cfg(feature = "hashbrown")]
+    inner: hash_map::VacantEntry<'a, TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>,
 }
 
 /// A view into a single location in a `RawMap`, which may be vacant or occupied.
diff --git a/test b/test
index ad559337811660933ab01a01c3c4622304f84cd6..f63590429b8cd441da3e4120c04cc0f4de06470e 100644 (file)
--- a/test
+++ b/test
@@ -2,9 +2,25 @@
 set -e
 export RUSTFLAGS="-D warnings"
 export RUSTDOCFLAGS="-D warnings"
-cargo +1.34.0 test
-cargo +1.34.0 test --release
+run_tests() {
+       for release in "" "--release"; do
+               cargo $1 test $release --no-default-features --features hashbrown
+               cargo $1 test $release --features hashbrown
+               # (2>/dev/null because otherwise you’ll keep seeing errors and double-guessing whether they were supposed to happen or whether the script failed to exit nonzero.)
+               ! 2>/dev/null cargo $1 test $release --no-default-features || ! echo "'cargo $1 test $release --no-default-features' failed to fail (sorry, its stderr is suppressed, try it manually)"
+               cargo $1 test $release
+       done
+}
+
+# We’d like to test with the oldest declared-supported version of *all* our dependencies.
+# That means Rust 1.36.0 + hashbrown 0.1.1.
+# Hence the different lock file.
+# (Also Rust 1.36.0 can’t read the latest lock file format.)
+cp test-oldest-Cargo.lock Cargo.lock
+run_tests +1.36.0
+rm Cargo.lock
+run_tests
+
 cargo clippy
-cargo test
-cargo test --release
+cargo bench
 cargo doc
diff --git a/test-oldest-Cargo.lock b/test-oldest-Cargo.lock
new file mode 100644 (file)
index 0000000..40aeb75
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "anymap"
+version = "1.0.0-beta.1"
+dependencies = [
+ "hashbrown 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "hashbrown"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scopeguard"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum byteorder 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+"checksum hashbrown 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dfb69c301cead891d010536c7d1b3affe792f5a2be5d6fbd0fcaf7fa0976962"
+"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"