1 # ``AnyMap``, a safe and convenient store for one value of each type
3 ``AnyMap
`` is a type-safe wrapper around
``HashMap<TypeId, Box<dyn Any>>
`` that lets you not worry about
``TypeId
`` or downcasting, but just get on with storing one each of a bag of diverse types, which is really useful for extensibility in some sorts of libraries.
7 If you’re familiar with Go and Go web frameworks, you may have come across the common “environment” pattern for storing data related to the request. It’s typically something like
``map[string]interface{}
`` and is accessed with arbitrary strings which may clash and type assertions which are a little unwieldy and must be used very carefully. (Personally I would consider that it is just *asking* for things to blow up in your face.) In a language like Go, lacking in generics, this is the best that can be done; such a thing cannot possibly be made safe without generics.
9 As another example of such an interface, JavaScript objects are exactly the same—a mapping of string keys to arbitrary values. (There it is actually *more* dangerous, because methods and fields/attributes/properties are on the same plane—though it’s *possible* to use
`Map` these days.)
11 Fortunately, we can do better than these things in Rust. Our type system is quite equal to easy, robust expression of such problems.
16 let mut data = anymap::AnyMap::new();
17 assert_eq!(data.get(), None::<&i32>);
19 assert_eq!(data.get(), Some(&42i32));
21 assert_eq!(data.get::<i32>(), None);
23 #[derive(Clone, PartialEq, Debug)]
28 assert_eq!(data.get::<Foo>(), None);
29 data.insert(Foo { str: format!("foo") });
30 assert_eq!(data.get(), Some(&Foo { str: format!("foo") }));
31 data.get_mut::<Foo>().map(|foo| foo.str.push('t'));
32 assert_eq!(&*data.get::<Foo>().unwrap().str, "foot");
37 - Store up to one value for each type in a bag.
38 - Add
`Send` or
`Send + Sync` bounds.
39 - You can opt into making the map
`Clone`. (In theory you could add all kinds of other functionality, but you can’t readily make this work *generically*, and the bones of it are simple enough that it becomes better to make your own extension of
`Any` and reimplement
`AnyMap`.)
42 ## Cargo features/dependencies/usage
44 Typical Cargo.toml usage, providing
`anymap::AnyMap` *et al.* backed by
`std::collections::HashMap`:
48 anymap = "1.0.0-beta.2"
51 No-std usage, providing
`anymap::hashbrown::AnyMap` *et al.* (note the different path, required because Cargo features are additive) backed by
`alloc` and the
[hashbrown](https://rust-lang.github.io/hashbrown) crate:
55 anymap = { version = "1.0.0-beta.2", default-features = false, features = ["hashbrown"] }
58 **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, 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`).
60 ## Unsafe code in this library
62 This library uses a fair bit of unsafe code for several reasons:
64 - To support
`CloneAny`, unsafe code is currently required (because the downcast methods are defined on
`dyn Any` rather than being trait methods, and upcasting is an incomplete feature in rustc); if you wanted to ditch
`Clone` support this unsafety could be removed.
66 - For
`dyn CloneAny + Send` and
`dyn CloneAny + Send + Sync`’s
`Clone` implementation, an unsafe block is used to attach the auto traits where safe code used to be used, in order to avoid a
[spurious future-compatibility lint](https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013).
68 - In the interests of performance, type ID checks are skipped as unnecessary because of the invariants of the data structure (though this does come at the cost of
`Map::{as_raw_mut, into_raw}` being marked unsafe).
70 It is possible to remove all unsafe code at the cost of only
`CloneAny` functionality and a little performance. The
`safe` branch in the Git repository contains a couple of commits demonstrating the concept. It’s quite straightforward; the core of this library is very simple and perfectly safe.
74 **Authorship:**
[Chris Morgan](https://chrismorgan.info/) is the author and maintainer of this library.
76 **Licensing:** this library is distributed under the terms of the
77 [Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0), the
78 [MIT License](https://opensource.org/licenses/MIT) and the
79 [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), at your choice.
80 See [COPYING](COPYING) for details.