anymap/README.md

50 lines
4 KiB
Markdown
Raw Normal View History

``AnyMap``, a safe and convenient store for one value of each type
==================================================================
[![Build Status](https://travis-ci.org/chris-morgan/anymap.svg?branch=master)](https://travis-ci.org/chris-morgan/anymap)
2014-06-12 17:29:24 +10:00
If youre familiar with Go and Go web frameworks, you may have come across the common “environment” pattern for storing data related to the request. Its 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.
2014-06-12 17:29:24 +10:00
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.)
2014-06-12 17:29:24 +10:00
Fortunately, we can do better than these things in Rust. Our type system is quite equal to easy, robust expression of such problems.
2014-06-12 17:29:24 +10:00
The ``AnyMap`` type is a friendly wrapper around a ``HashMap<TypeId, Box<Any>>``, exposing a nice, easy typed interface, perfectly safe and absolutely robust.
2014-06-12 17:29:24 +10:00
What this means is that in an ``AnyMap`` you may store zero or one values for every type.
Instructions
------------
Cargo all the way: it is `anymap` on crates.io.
2017-01-24 04:07:10 +11:00
Unsafe code in this library
---------------------------
This library uses a fair bit of unsafe code for several reasons:
- To support Any and CloneAny, unsafe code is required (because of how the `downcast` methods are defined in `impl Any` rather than being trait methods; I think this is kind of a historical detail of the structure of `std::any::Any`); if you wanted to ditch `Clone` support this unsafety could be removed.
- In the interests of performance, skipping various checks that are unnecessary because of the invariants of the data structure (no need to check the type ID when its been statically ensured by being used as the hash map key) and simplifying hashing (type IDs are already good hashes, no need to mangle them through SipHash).
Its not possible to remove all unsafety from this library without also removing some of the functionality. Still, at the cost of the `CloneAny` functionality, the raw interface and maybe the concurrency support, you can definitely remove all unsafe code. Heres how you could do it:
- Remove the genericness of it all;
- Merge `anymap::raw` into the normal interface, flattening it;
- Change things like `.map(|any| unsafe { any.downcast_unchecked() })` to `.and_then(|any| any.downcast())` (performance cost: one extra superfluous type ID comparison, indirect);
- Ditch the `TypeIdHasher` since transmuting a `TypeId` is right out (cost: SIP mangling of a u64 on every access).
Yeah, the performance costs of going safe are quite small. The more serious matters are the loss of `Clone` and maybe `Send + Sync`.
But frankly, if you wanted to do all this itd be easier and faster to write it from scratch. The core of the library is actually really simple and perfectly safe, as can be seen in [`src/lib.rs` in the first commit](https://github.com/chris-morgan/anymap/tree/a294948f57dee47bb284d6a3ae1b8f61a902a03c/src/lib.rs) (note that that code wont run without a few syntactic alterations; it was from well before Rust 1.0 and has things like `Any:'static` where now we have `Any + 'static`).
Add the BlueOak-1.0.0 license I prefer to use BlueOak-1.0.0 now; It wasn’t around back in 2017. There are a number of commits in this repository not made by me, all from before Rust 1.0.0: • f1710353a039c1d5e95d15bb54effd7cab8e047f (Robert Straw; trivial: matching std enum namespacing breakage) • de091453093fb5139de71b085411d2fad1a52275 (Robert Straw; trivial: std enum namespacing breakage) • 2e37f0d1aebbd0c56acd0de7b46d14cd71d3e134 (Jonathan Reem; added AnyMap::contains, which had become obvious for Rust collection parity) • 8b30c87fe6cf514544d8bc68989631daac8aeec1 (tivek; trivial: Rust syntax change in integer literal inference) • c9d196be5f40fb0c6e53c65fa072aea60de8c3f1 (Jonathan Reem; trivial: version bump) • 330bc5aa1e4b1644a19b3751ee3b65c849447005 (Jonathan Reem; not creative and largely no longer present: introduced Cargo support, tweaked Makefile) • a9b1e31b7062e42248347805cbee662efe0eb713 (Tomas Sedovic; nigh-trivial and no longer present: Collection and Mutable trait implementations) • eecc4a4b758ae743486cfe4254cefd136f829391 (Jonathan Reem; trivial: Rust syntax change) • d51aff506409d7ffa2de275cd4e3029a88de1e2a (Jonathan Reem; trivial: rustc lint change) • 56113c63b028e7d215f1c16cf90d9c5d902df477 (Jonathan Reem; trivial: Rust syntax change) All but one of these are definitely trivial, obvious, and in the context of the project and ecosystem not creative works (⅌ copyright doctrine definition); or else no longer present. The one arguable exception is 2e37f0d1aebbd0c56acd0de7b46d14cd71d3e134, adding AnyMap::contains, since I hadn’t added a contains method; but its *definition* is trivial with only one possible implementation, and subsequent to that time I did go through and check for parity with HashMap methods, to say nothing of the code having changed shape quite a bit since then too. Therefore I’m content to consider it immaterial for relicensing.
2022-01-25 12:40:30 +11:00
## Colophon
2014-06-12 17:29:24 +10:00
Add the BlueOak-1.0.0 license I prefer to use BlueOak-1.0.0 now; It wasn’t around back in 2017. There are a number of commits in this repository not made by me, all from before Rust 1.0.0: • f1710353a039c1d5e95d15bb54effd7cab8e047f (Robert Straw; trivial: matching std enum namespacing breakage) • de091453093fb5139de71b085411d2fad1a52275 (Robert Straw; trivial: std enum namespacing breakage) • 2e37f0d1aebbd0c56acd0de7b46d14cd71d3e134 (Jonathan Reem; added AnyMap::contains, which had become obvious for Rust collection parity) • 8b30c87fe6cf514544d8bc68989631daac8aeec1 (tivek; trivial: Rust syntax change in integer literal inference) • c9d196be5f40fb0c6e53c65fa072aea60de8c3f1 (Jonathan Reem; trivial: version bump) • 330bc5aa1e4b1644a19b3751ee3b65c849447005 (Jonathan Reem; not creative and largely no longer present: introduced Cargo support, tweaked Makefile) • a9b1e31b7062e42248347805cbee662efe0eb713 (Tomas Sedovic; nigh-trivial and no longer present: Collection and Mutable trait implementations) • eecc4a4b758ae743486cfe4254cefd136f829391 (Jonathan Reem; trivial: Rust syntax change) • d51aff506409d7ffa2de275cd4e3029a88de1e2a (Jonathan Reem; trivial: rustc lint change) • 56113c63b028e7d215f1c16cf90d9c5d902df477 (Jonathan Reem; trivial: Rust syntax change) All but one of these are definitely trivial, obvious, and in the context of the project and ecosystem not creative works (⅌ copyright doctrine definition); or else no longer present. The one arguable exception is 2e37f0d1aebbd0c56acd0de7b46d14cd71d3e134, adding AnyMap::contains, since I hadn’t added a contains method; but its *definition* is trivial with only one possible implementation, and subsequent to that time I did go through and check for parity with HashMap methods, to say nothing of the code having changed shape quite a bit since then too. Therefore I’m content to consider it immaterial for relicensing.
2022-01-25 12:40:30 +11:00
**Authorship:** [Chris Morgan](https://chrismorgan.info/) is the author and maintainer of this library.
2014-06-12 17:29:24 +10:00
Add the BlueOak-1.0.0 license I prefer to use BlueOak-1.0.0 now; It wasn’t around back in 2017. There are a number of commits in this repository not made by me, all from before Rust 1.0.0: • f1710353a039c1d5e95d15bb54effd7cab8e047f (Robert Straw; trivial: matching std enum namespacing breakage) • de091453093fb5139de71b085411d2fad1a52275 (Robert Straw; trivial: std enum namespacing breakage) • 2e37f0d1aebbd0c56acd0de7b46d14cd71d3e134 (Jonathan Reem; added AnyMap::contains, which had become obvious for Rust collection parity) • 8b30c87fe6cf514544d8bc68989631daac8aeec1 (tivek; trivial: Rust syntax change in integer literal inference) • c9d196be5f40fb0c6e53c65fa072aea60de8c3f1 (Jonathan Reem; trivial: version bump) • 330bc5aa1e4b1644a19b3751ee3b65c849447005 (Jonathan Reem; not creative and largely no longer present: introduced Cargo support, tweaked Makefile) • a9b1e31b7062e42248347805cbee662efe0eb713 (Tomas Sedovic; nigh-trivial and no longer present: Collection and Mutable trait implementations) • eecc4a4b758ae743486cfe4254cefd136f829391 (Jonathan Reem; trivial: Rust syntax change) • d51aff506409d7ffa2de275cd4e3029a88de1e2a (Jonathan Reem; trivial: rustc lint change) • 56113c63b028e7d215f1c16cf90d9c5d902df477 (Jonathan Reem; trivial: Rust syntax change) All but one of these are definitely trivial, obvious, and in the context of the project and ecosystem not creative works (⅌ copyright doctrine definition); or else no longer present. The one arguable exception is 2e37f0d1aebbd0c56acd0de7b46d14cd71d3e134, adding AnyMap::contains, since I hadn’t added a contains method; but its *definition* is trivial with only one possible implementation, and subsequent to that time I did go through and check for parity with HashMap methods, to say nothing of the code having changed shape quite a bit since then too. Therefore I’m content to consider it immaterial for relicensing.
2022-01-25 12:40:30 +11:00
**Licensing:** this library is distributed under the terms of the
[Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0), the
[MIT License](https://opensource.org/licenses/MIT) and the
[Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), at your choice.
See [COPYING](COPYING) for details.