no_std support

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.
This commit is contained in:
Chris Morgan 2022-01-25 21:24:48 +11:00
parent 98f2816e62
commit 0a1c85f865
9 changed files with 117 additions and 9 deletions

View 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

View file

@ -12,7 +12,10 @@
- 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

View 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 }

View file

@ -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 its 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 youre 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:

View 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 {

View 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;

View 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.

24
test
View file

@ -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 youll 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
}
# Wed 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 cant 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

32
test-oldest-Cargo.lock generated Normal file
View file

@ -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"