{"id":23416218,"url":"https://github.com/bluk/gen_value","last_synced_at":"2025-10-10T02:37:32.308Z","repository":{"id":57673424,"uuid":"481736374","full_name":"bluk/gen_value","owner":"bluk","description":"Generational indexes and values.","archived":false,"fork":false,"pushed_at":"2023-10-02T21:15:46.000Z","size":161,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"trunk","last_synced_at":"2024-05-02T02:57:24.235Z","etag":null,"topics":["ecs","generation","generational","generational-index","rust","vector"],"latest_commit_sha":null,"homepage":"https://docs.rs/gen_value/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bluk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-04-14T20:11:39.000Z","updated_at":"2022-04-15T02:00:58.000Z","dependencies_parsed_at":"2023-10-03T02:43:59.789Z","dependency_job_id":null,"html_url":"https://github.com/bluk/gen_value","commit_stats":{"total_commits":37,"total_committers":2,"mean_commits":18.5,"dds":"0.43243243243243246","last_synced_commit":"00ada3677afe3b76a33f87057c2c708452058d4c"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluk%2Fgen_value","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluk%2Fgen_value/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluk%2Fgen_value/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluk%2Fgen_value/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bluk","download_url":"https://codeload.github.com/bluk/gen_value/tar.gz/refs/heads/trunk","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247994102,"owners_count":21030049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecs","generation","generational","generational-index","rust","vector"],"created_at":"2024-12-22T22:14:03.335Z","updated_at":"2025-10-10T02:37:27.291Z","avatar_url":"https://github.com/bluk.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GenValue\n\nGenValue is a library for using values and indexes with a generation in a\n`Vec`.\n\nGenerational indexes give a `Vec` an identifier which can always refer to the\nsame value (unless the value is removed) while re-using memory efficiently.\n\nGenerational indexes are commonly used in [entity component systems][wiki_ecs].\n\nThis library is a simple, naive, and general purpose implementation of the idea.\nIn most systems, an optimized and performant version of the idea (which depends on the\nuse case) can be built.\n\n## Documentation\n\n* [Latest API Docs][docs_rs_gen_value]\n\n## Installation\n\n```toml\n[dependencies]\ngen_value = \"0.7.0\"\n```\n\n## Examples\n\nUsing a `Vec` with generations associated with values and indexes.\n\n```rust\nuse gen_value::{vec::GenVec, Error};\n\n#[derive(Debug, PartialEq)]\nstruct Value\u003cT\u003e(T);\n\n// * The element type is `Value\u003cu32\u003e`.\n// * The generation type by default is a `usize`.\n// * The index type by default is a `usize`.\n// * The generational index type (index type, generation type)\n//   by default is a tuple `(usize, usize)`.\nlet mut gen_vec = GenVec::\u003cValue\u003cu32\u003e\u003e::default();\n\n// Insert entities\nlet first_entity_index: (usize, usize) = gen_vec.insert(Value(42))?;\nlet other_entity_index = gen_vec.insert(Value(9))?;\nassert_eq!(gen_vec.get(first_entity_index).ok(), Some(\u0026Value(42)));\nassert_eq!(*gen_vec.get(other_entity_index)?, Value(9));\nassert_eq!(gen_vec.len(), 2);\n\n// Remove first entity's value\ngen_vec.remove(first_entity_index)?;\n\n// Other entity can still be retrieved with same index \nassert_eq!(*gen_vec.get(other_entity_index)?, Value(9));\n// Cannot get first entity's value\nassert_eq!(gen_vec.get(first_entity_index).ok(), None);\n// `Vec` length is still 2\nassert_eq!(gen_vec.len(), 2);\n\n// Insert last entity\nlet last_entity_index = gen_vec.insert(Value(3))?;\nassert_eq!(*gen_vec.get(other_entity_index)?, Value(9));\nassert_eq!(*gen_vec.get(last_entity_index)?, Value(3));\n// First entity index still cannot access a value\nassert!(gen_vec.get(first_entity_index).is_err());\nassert_eq!(gen_vec.len(), 2);\n\n// The indexes and generations used\nassert_eq!(first_entity_index, (0, 0));\nassert_eq!(other_entity_index, (1, 0));\n// The last entity re-used index 0 but with a newer generation\nassert_eq!(last_entity_index, (0, 1));\n\nOk::\u003c_, Error\u003e(())\n```\n\n## Motivation\n\nIt is relatively fast to access an element in a `Vec`. Given an index, a\nrelatively simple offset calculation is made from the beginning of the vector and\nan element can be read. Even better, because a vector's elements are located\nadjacent in memory, vectors are ideal for CPU cache prefetching.\n\nIn comparision, a `HashMap` requires hashing a key and then possibly following\nmultiple pointers to find the element. A `BTreeMap` may have to do several\ncomparisions before finding the entry with the desired key. Both should be\nrelatively slower than accessing an element by an index in a `Vec`.\n\nOn the other hand, a `Vec` index is not usually a stable identifier for an\nelement. By inserting a new element or removing an element, the positions for\nthe elements usually change, so an index may access different elements over\ntime.\n\nFor a `HashMap` and a `BTreeMap`, keys are stable identifiers for\nvalues (as long as the keys follow the documented invariants). If a key is used\nto store a value in a map, an equal key can be used to retrieve the value later\n(assuming the value was not removed). The keys can be copied to various parts of\nthe program and still be used later.\n\nIn many use cases, mapping stable identifiers to values is desirable.\n\nHow can we have stable identifiers and yet try to keep the performance\nproperties for a `Vec`?\n\n### Never Move Elements\n\nOne way for a `Vec` to have stable identifiers is to never move elements. Once a\nvalue is pushed to the end of the `Vec`, the value will always have the same\nindex. All operations which move elements can be disallowed (e.g. inserting a\nnew element at a position).\n\nStill, removing an element from a collection is important functionality. A\npossible solution is to add a \"removed\" tombstone value by using a\n`Vec\u003cOption\u003cT\u003e\u003e`. Values are pushed as `Some(T)` and when removed will be set to\n`None`. Indexes become stable identifiers for elements.\n\nUnfortunately, there is an increasing amount of unused memory as elements are\nset to `None` when being removed.\n\n### Indexes and Values with Generations\n\nGenerational indexes are a possible solution to maximizing the usage of memory\nwhile keeping elements in the same position. Every element in the `Vec` is\nassociated with a generation value, which is usually an integer. A generational\nindex is an index with a generation value. Depending on if the generation values\nmatch or if one generation value is greater than another, an operation will\nsucceed.\n\n\u003e A `Vec\u003cT\u003e` becomes `Vec\u003c(G, T)\u003e` and an individual element index `I` becomes\n\u003e `(I, G)` where `G` is the generation type.\n\nSo in a `Vec\u003c(usize, Person)\u003e`, the element at index `9` could have `2` for the\ngeneration and a `Person` value. If the generational index `(9, 2)` is used, the\nelement at the 9th offset is read. Then, the generation associated with the\n`Person` value is compared with the generation associated with the index. Since\nboth are `2`, the operation succeeds. If the generational index was `(9, 1)`,\nthen the operation would fail because the generations would not match.\n\nSuppose there is a generational index (`(3,1)`) and the `Vec` at index `3` has a\nvalue with generation `1`. When the element is \"removed\", the generation\nassociated with the value in the `Vec` is increased. So the `Vec` would have\ngeneration `2` at index `3`. The generational index (`(3,1)`) could no longer be\nused to access the `Vec` value at index `3` because the generations are no\nlonger equal. While the value is not technically dropped (in Rust's\nterminology), the value is effectively removed from access.\n\nThe index with the latest generation is available to store a new value, so\nmemory is reused instead of left unused. An entity using the previous generation\nof an index will no longer be able to access the removed value while an entity\nusing the latest generation of the index will retrieve the value at the index.\n\nThe allocation of new indexes and increases in generations must be logically\ncorrect for program correctness.\n\n## Similar Projects\n\n* [generational-arena][generational_arena]\n* [gen-vec][gen_vec]\n\nBoth crates are inspired by [Catherine West's excellent closing keynote for\nRustConf 2018][rustconf_2018_closing_keynote].\n\nThe major difference between the other crates is this crate's direct control of\nthe index types and generation types through type parameters. If a use case only\nneeds the generation to be `u8`, this crate can use a `u8`. The generational\nindex type itself can be specified (instead of using a single `Index` type or\n`(I, G)` tuple) for some type safety use cases. Each crate chose different APIs\nto expose.\n\n## License\n\nLicensed under either of [Apache License, Version 2.0][LICENSE_APACHE] or [MIT\nLicense][LICENSE_MIT] at your option.\n\n### Contributions\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n[LICENSE_APACHE]: LICENSE-APACHE\n[LICENSE_MIT]: LICENSE-MIT\n[wiki_ecs]: https://en.wikipedia.org/wiki/Entity_component_system\n[docs_rs_gen_value]: https://docs.rs/gen_value/\n[BTreeMap]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html\n[Vec]: https://doc.rust-lang.org/std/vec/struct.Vec.html\n[generational_arena]: https://crates.io/crates/generational-arena\n[gen_vec]: https://crates.io/crates/gen-vec\n[rustconf_2018_closing_keynote]: https://www.youtube.com/watch?v=aKLntZcp27M \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluk%2Fgen_value","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbluk%2Fgen_value","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluk%2Fgen_value/lists"}