├── .cargo-rdme.toml ├── .clippy.toml ├── .codecov.yml ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .rustfmt.toml ├── .taplo.toml ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE.md ├── README.md ├── benches ├── archery_shared_pointer_arc.rs ├── archery_shared_pointer_arct.rs ├── archery_shared_pointer_rc.rs ├── std_arc.rs └── std_rc.rs ├── examples └── example.rs ├── images └── archery.svg ├── release-notes.md ├── src ├── lib.rs └── shared_pointer │ ├── kind │ ├── arc │ │ ├── mod.rs │ │ └── test.rs │ ├── arct │ │ ├── mod.rs │ │ └── test.rs │ ├── mod.rs │ └── rc │ │ ├── mod.rs │ │ └── test.rs │ ├── mod.rs │ └── test.rs └── tools ├── check.sh ├── codecov.sh ├── release.sh └── utils.sh /.cargo-rdme.toml: -------------------------------------------------------------------------------- 1 | line-terminator = "lf" 2 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | avoid-breaking-exported-api = false 2 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "80...100" 9 | 10 | status: 11 | project: 12 | default: 13 | target: auto 14 | threshold: 10% 15 | patch: no 16 | changes: no 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '0 19 * * 3' 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | # Miri breaks frequently, so we pin it to a specific nightly version. 14 | MIRI_TOOLCHAIN: nightly-2025-03-26 15 | 16 | jobs: 17 | check: 18 | runs-on: ${{ matrix.os }} 19 | 20 | strategy: 21 | matrix: 22 | os: [ubuntu-latest, macos-latest, windows-latest] 23 | 24 | steps: 25 | - name: Install miri 26 | uses: dtolnay/rust-toolchain@stable 27 | with: 28 | toolchain: ${{ env.MIRI_TOOLCHAIN }} 29 | components: miri, rust-src 30 | 31 | - name: Install rust 32 | uses: dtolnay/rust-toolchain@stable 33 | with: 34 | components: rustfmt, rust-src 35 | 36 | - name: Install cargo plugins 37 | run: | 38 | cargo install cargo-rdme 39 | cargo install cargo-machete 40 | cargo install taplo-cli 41 | cargo install cargo-deadlinks 42 | cargo install cargo-criterion 43 | 44 | - name: Checkout repository 45 | uses: actions/checkout@v4 46 | 47 | - name: Check everything 48 | run: bash ./tools/check.sh basic doc_url_links unused_deps packaging fmt toml_fmt readme 49 | 50 | - name: Code coverage 51 | if: ${{ runner.os == 'Linux' }} 52 | run: | 53 | cargo install cargo-tarpaulin 54 | ./tools/codecov.sh --xml 55 | bash <(curl -s https://codecov.io/bash) 56 | 57 | msrv: 58 | runs-on: ubuntu-latest 59 | 60 | steps: 61 | - name: Install rust 62 | uses: dtolnay/rust-toolchain@stable 63 | 64 | - name: Install cargo plugins 65 | run: cargo install cargo-msrv 66 | 67 | - name: Checkout repository 68 | uses: actions/checkout@v4 69 | 70 | - name: Check the minimum supported rust version 71 | run: bash ./tools/check.sh msrv 72 | 73 | clippy: 74 | runs-on: ubuntu-latest 75 | 76 | steps: 77 | - name: Install rust 78 | uses: dtolnay/rust-toolchain@stable 79 | with: 80 | components: clippy 81 | 82 | - name: Checkout repository 83 | uses: actions/checkout@v4 84 | 85 | - name: Run clippy 86 | run: bash ./tools/check.sh clippy 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | 3 | /target/ 4 | *.rs.bk 5 | 6 | /Cargo.lock 7 | /tarpaulin-report.html 8 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_field_init_shorthand = true 2 | use_try_shorthand = true 3 | use_small_heuristics = "max" 4 | 5 | # TODO Awaiting stabilization 6 | # comment_width = 100 7 | # match_arm_blocks = false 8 | # struct_field_align_threshold = 4 9 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | include = ["**/*.toml"] 2 | exclude = ["target/**"] 3 | 4 | [formatting] 5 | column_width = 100 6 | indent_string = ' ' 7 | allowed_blank_lines = 1 8 | 9 | [[rule]] 10 | formatting = { reorder_keys = true } 11 | keys = [ 12 | "workspace.dependencies", 13 | "dependencies", 14 | "dev-dependencies", 15 | "build-dependencies", 16 | "lints.clippy", 17 | ] 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to archery 2 | 3 | Thank you for your interest in contributing to archery. We appreciate it! 4 | 5 | It is a good idea for you to open an issue before you start working on non-trivial features. This way we can discuss if 6 | the feature is desirable and how to best design and implement it. 7 | 8 | ## Useful tools 9 | 10 | If you are contributing with a pull request you probably want to know about these scripts: 11 | 12 | * [`./tools/check.sh`](tools/check.sh) — Checks that everything is fine. This includes checking that everything 13 | builds, the unit tests pass, and the code is correctly formatted. If you need to format the code run 14 | `cargo fmt`. 15 | * [`./tools/codecov.sh`](tools/codecov.sh) — Creates a code coverage report. There is not a strict code coverage 16 | threshold, but we do want pretty much everything tested. 17 | * [`cargo rdme`](https://crates.io/crates/cargo-rdme) — Updates the README with the crate’s rustdoc. 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "archery" 3 | description = "Abstract over the atomicity of reference-counting pointers" 4 | version = "1.2.3-pre" 5 | authors = ["Diogo Sousa "] 6 | 7 | edition = "2024" 8 | rust-version = "1.85.0" 9 | 10 | homepage = "https://github.com/orium/archery" 11 | repository = "https://github.com/orium/archery" 12 | documentation = "https://docs.rs/archery" 13 | readme = "README.md" 14 | 15 | keywords = ["rc", "arc", "reference-counting", "no_std"] 16 | 17 | categories = ["concurrency", "memory-management", "rust-patterns"] 18 | 19 | license = "MIT" 20 | 21 | # What to include when packaging. 22 | include = ["/src/**/*.rs", "/Cargo.toml", "/LICENSE.md", "/README.md", "/release-notes.md"] 23 | 24 | [badges] 25 | codecov = { repository = "orium/archery", branch = "main", service = "github" } 26 | 27 | [dependencies] 28 | serde = { version = "1.0.228", optional = true, default-features = false } 29 | triomphe = { version = "0.1.14", optional = true, default-features = false } 30 | 31 | [dev-dependencies] 32 | criterion = { version = "0.7.0", features = ["html_reports"] } 33 | pretty_assertions = "1.4.1" 34 | serde_json = "1.0.145" 35 | static_assertions = "1.1.0" 36 | 37 | [features] 38 | fatal-warnings = [] 39 | triomphe = ["dep:triomphe"] 40 | serde = ["dep:serde"] 41 | 42 | [lints.clippy] 43 | all = { level = "warn", priority = -2 } 44 | correctness = { level = "deny", priority = -1 } 45 | pedantic = { level = "warn", priority = -2 } 46 | 47 | explicit-deref-methods = "allow" 48 | if-not-else = "allow" 49 | inline-always = "allow" 50 | match-bool = "allow" 51 | missing-errors-doc = "allow" 52 | missing-safety-doc = "allow" 53 | module-name-repetitions = "allow" 54 | partialeq-ne-impl = "allow" 55 | similar-names = "allow" 56 | single-match-else = "allow" 57 | use-self = "allow" 58 | wildcard-imports = "allow" 59 | 60 | [lints.rustdoc] 61 | # TODO This is only needed because `cargo-rdme` requires a path like `crate::⋯`. Once that limitation is lifted we 62 | # can remove this. 63 | redundant-explicit-links = "allow" 64 | 65 | [lib] 66 | # Disable libtest to make sure criterion can parse the command line flags. 67 | # See https://bheisler.github.io/criterion.rs/book/faq.html and https://github.com/rust-lang/rust/issues/47241. 68 | bench = false 69 | 70 | [[bench]] 71 | name = "std_rc" 72 | path = "benches/std_rc.rs" 73 | harness = false 74 | 75 | [[bench]] 76 | name = "std_arc" 77 | path = "benches/std_arc.rs" 78 | harness = false 79 | 80 | [[bench]] 81 | name = "archery_shared_pointer_rc" 82 | path = "benches/archery_shared_pointer_rc.rs" 83 | harness = false 84 | 85 | [[bench]] 86 | name = "archery_shared_pointer_arc" 87 | path = "benches/archery_shared_pointer_arc.rs" 88 | harness = false 89 | 90 | [[bench]] 91 | name = "archery_shared_pointer_arct" 92 | path = "benches/archery_shared_pointer_arct.rs" 93 | harness = false 94 | required-features = ["triomphe"] 95 | 96 | [package.metadata.docs.rs] 97 | features = ["triomphe", "serde"] 98 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Diogo Sousa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/orium/archery/workflows/CI/badge.svg)](https://github.com/orium/archery/actions?query=workflow%3ACI) 2 | [![Code Coverage](https://codecov.io/gh/orium/archery/branch/main/graph/badge.svg)](https://codecov.io/gh/orium/archery) 3 | [![Dependency status](https://deps.rs/repo/github/orium/archery/status.svg)](https://deps.rs/repo/github/orium/archery) 4 | [![crates.io](https://img.shields.io/crates/v/archery.svg)](https://crates.io/crates/archery) 5 | [![Downloads](https://img.shields.io/crates/d/archery.svg)](https://crates.io/crates/archery) 6 | [![Github stars](https://img.shields.io/github/stars/orium/archery.svg?logo=github)](https://github.com/orium/archery/stargazers) 7 | [![Documentation](https://docs.rs/archery/badge.svg)](https://docs.rs/archery/) 8 | [![License](https://img.shields.io/crates/l/archery.svg)](./LICENSE.md) 9 | 10 | 11 | # `archery` 12 | 13 | 14 | 15 | `archery` is a rust library that offers a way to abstraction over 16 | [`Rc`](https://doc.rust-lang.org/stable/alloc/rc/struct.Rc.html) and 17 | [`Arc`](https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html) smart pointers. 18 | This allows you to create data structures where the pointer type is parameterizable, so you can 19 | [avoid the overhead of `Arc`](https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html#thread-safety) 20 | when you don’t need to share data across threads. 21 | 22 | In languages that supports 23 | [higher-kinded polymorphism](https://en.wikipedia.org/wiki/Type_class#Higher-kinded_polymorphism) 24 | this would be simple to achieve without any library, but 25 | [rust does not support that yet](https://github.com/rust-lang/rfcs/issues/324). 26 | To mimic higher-kinded polymorphism `archery` implements the approach suggested by 27 | Joshua Liebow-Feeser in 28 | “[Rust has higher kinded types already… sort of](https://joshlf.com/post/2018/10/18/rust-higher-kinded-types-already/)”. 29 | While [other approaches](#alternative-approaches) exist, they seem to always offer poor 30 | ergonomics for the user. 31 | 32 | ## Setup 33 | 34 | To use `archery` add the following to your `Cargo.toml`: 35 | 36 | ```toml 37 | [dependencies] 38 | archery = "" 39 | ``` 40 | 41 | ## Using `archery` 42 | 43 | `archery` defines a [`SharedPointer`](https://docs.rs/archery/latest/archery/shared_pointer/struct.SharedPointer.html) 44 | that receives the [kind of pointer](https://docs.rs/archery/latest/archery/shared_pointer/kind/trait.SharedPointerKind.html) 45 | as a type parameter. This gives you a convenient and ergonomic way to abstract the pointer 46 | type away. 47 | 48 | ### Example 49 | 50 | Declare a data structure with the pointer kind as a type parameter bounded by 51 | [`SharedPointerKind`](https://docs.rs/archery/latest/archery/shared_pointer/kind/trait.SharedPointerKind.html): 52 | 53 | ```rust 54 | use archery::*; 55 | 56 | struct KeyValuePair { 57 | pub key: SharedPointer, 58 | pub value: SharedPointer, 59 | } 60 | 61 | impl KeyValuePair { 62 | fn new(key: K, value: V) -> KeyValuePair { 63 | KeyValuePair { 64 | key: SharedPointer::new(key), 65 | value: SharedPointer::new(value), 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | To use it just plug-in the kind of pointer you want: 72 | 73 | ```rust 74 | let pair: KeyValuePair<_, _, RcK> = 75 | KeyValuePair::new("António Variações", 1944); 76 | 77 | assert_eq!(*pair.value, 1944); 78 | ``` 79 | 80 | ### `triomphe::Arc` 81 | 82 | You can also use [`triomphe::Arc`](https://docs.rs/triomphe/latest/triomphe/struct.Arc.html) 83 | as the backing implementation of a [`SharedPointer`](https://docs.rs/archery/latest/archery/shared_pointer/struct.SharedPointer.html). 84 | This is generally faster than [`std::sync::Arc`](https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html). 85 | Read [`triomphe`’s crate documentation](https://docs.rs/triomphe/latest/triomphe/) to learn more 86 | about it. 87 | 88 | To use it you need to enable the `triomphe` feature in `archery`. Use `ArcTK` as the pointer 89 | kind in [`SharedPointer`](https://docs.rs/archery/latest/archery/shared_pointer/struct.SharedPointer.html). 90 | 91 | ### Serialization 92 | 93 | We support serialization through [serde](https://crates.io/crates/serde). To use it 94 | enable the `serde` feature. To do so change the archery dependency in your `Cargo.toml` to 95 | 96 | ```toml 97 | [dependencies] 98 | archery = { version = "", features = ["serde"] } 99 | ``` 100 | ## Limitations 101 | 102 | Currently it is not possible to have unsized types inside a 103 | [`SharedPointer`](https://docs.rs/archery/latest/archery/shared_pointer/struct.SharedPointer.html). As a workaround you can put the 104 | unsized type inside a [`Box`](https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html). 105 | 106 | ## Alternative approaches 107 | 108 | An alternative to the approach taken by `archery` is to use traits with associated types to encode 109 | type-level functions. This has been suggested 110 | [multiple](https://github.com/orium/rpds/issues/7#issuecomment-362635901) 111 | [times](https://joshlf.com/post/2018/10/18/rust-higher-kinded-types-already/#comment-4160863400), 112 | but offers ugly ergonomics (see 113 | [here](https://github.com/Marwes/rpds/blob/e482d5abbaa6c876d7c624e497affe7299bbeece/src/sequence/vector/mod.rs#L153) 114 | and [here](https://github.com/Marwes/rpds/blob/e482d5abbaa6c876d7c624e497affe7299bbeece/src/sequence/vector/mod.rs#L249)). 115 | 116 | 117 | -------------------------------------------------------------------------------- /benches/archery_shared_pointer_arc.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "fatal-warnings", deny(warnings))] 2 | 3 | use archery::*; 4 | use std::ops::Deref; 5 | 6 | use criterion::{Criterion, criterion_group, criterion_main}; 7 | use std::hint::black_box; 8 | 9 | fn archery_shared_pointer_arc_deref(c: &mut Criterion) { 10 | let limit = 200_000; 11 | 12 | c.bench_function("archery shared pointer arc deref", move |b| { 13 | b.iter(|| { 14 | let rc: SharedPointer<_, ArcK> = SharedPointer::new(42); 15 | 16 | for _ in 0..limit { 17 | black_box(rc.deref()); 18 | } 19 | 20 | rc 21 | }); 22 | }); 23 | } 24 | 25 | fn archery_shared_pointer_arc_clone(c: &mut Criterion) { 26 | let limit = 100_000; 27 | 28 | c.bench_function("archery shared pointer arc clone and drop", move |b| { 29 | b.iter_with_setup( 30 | || Vec::with_capacity(limit), 31 | |mut vec| { 32 | vec.resize(limit, SharedPointer::<_, ArcK>::new(42)); 33 | vec 34 | }, 35 | ); 36 | }); 37 | } 38 | 39 | criterion_group!(benches, archery_shared_pointer_arc_deref, archery_shared_pointer_arc_clone); 40 | criterion_main!(benches); 41 | -------------------------------------------------------------------------------- /benches/archery_shared_pointer_arct.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "fatal-warnings", deny(warnings))] 2 | 3 | use archery::*; 4 | use std::ops::Deref; 5 | 6 | use criterion::{Criterion, criterion_group, criterion_main}; 7 | use std::hint::black_box; 8 | 9 | fn archery_shared_pointer_arct_deref(c: &mut Criterion) { 10 | let limit = 200_000; 11 | 12 | c.bench_function("archery shared pointer arct deref", move |b| { 13 | b.iter(|| { 14 | let rc: SharedPointer<_, ArcTK> = SharedPointer::new(42); 15 | 16 | for _ in 0..limit { 17 | black_box(rc.deref()); 18 | } 19 | 20 | rc 21 | }); 22 | }); 23 | } 24 | 25 | fn archery_shared_pointer_arct_clone(c: &mut Criterion) { 26 | let limit = 100_000; 27 | 28 | c.bench_function("archery shared pointer arct clone and drop", move |b| { 29 | b.iter_with_setup( 30 | || Vec::with_capacity(limit), 31 | |mut vec| { 32 | vec.resize(limit, SharedPointer::<_, ArcTK>::new(42)); 33 | vec 34 | }, 35 | ); 36 | }); 37 | } 38 | 39 | criterion_group!(benches, archery_shared_pointer_arct_deref, archery_shared_pointer_arct_clone); 40 | criterion_main!(benches); 41 | -------------------------------------------------------------------------------- /benches/archery_shared_pointer_rc.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "fatal-warnings", deny(warnings))] 2 | 3 | use archery::*; 4 | use std::ops::Deref; 5 | 6 | use criterion::{Criterion, criterion_group, criterion_main}; 7 | use std::hint::black_box; 8 | 9 | fn archery_shared_pointer_rc_deref(c: &mut Criterion) { 10 | let limit = 200_000; 11 | 12 | c.bench_function("archery shared pointer rc deref", move |b| { 13 | b.iter(|| { 14 | let rc: SharedPointer<_, RcK> = SharedPointer::new(42); 15 | 16 | for _ in 0..limit { 17 | black_box(rc.deref()); 18 | } 19 | 20 | rc 21 | }); 22 | }); 23 | } 24 | 25 | fn archery_shared_pointer_rc_clone(c: &mut Criterion) { 26 | let limit = 100_000; 27 | 28 | c.bench_function("archery shared pointer rc clone and drop", move |b| { 29 | b.iter_with_setup( 30 | || Vec::with_capacity(limit), 31 | |mut vec| { 32 | vec.resize(limit, SharedPointer::<_, RcK>::new(42)); 33 | vec 34 | }, 35 | ); 36 | }); 37 | } 38 | 39 | criterion_group!(benches, archery_shared_pointer_rc_deref, archery_shared_pointer_rc_clone); 40 | criterion_main!(benches); 41 | -------------------------------------------------------------------------------- /benches/std_arc.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "fatal-warnings", deny(warnings))] 2 | 3 | use std::ops::Deref; 4 | use std::sync::Arc; 5 | 6 | use criterion::{Criterion, criterion_group, criterion_main}; 7 | use std::hint::black_box; 8 | 9 | fn std_arc_deref(c: &mut Criterion) { 10 | let limit = 200_000; 11 | 12 | c.bench_function("std arc deref", move |b| { 13 | b.iter(|| { 14 | let rc = Arc::new(42); 15 | 16 | for _ in 0..limit { 17 | black_box(rc.deref()); 18 | } 19 | 20 | rc 21 | }); 22 | }); 23 | } 24 | 25 | fn std_arc_clone(c: &mut Criterion) { 26 | let limit = 100_000; 27 | 28 | c.bench_function("std arc clone and drop", move |b| { 29 | b.iter_with_setup( 30 | || Vec::with_capacity(limit), 31 | |mut vec| { 32 | vec.resize(limit, Arc::new(42)); 33 | vec 34 | }, 35 | ); 36 | }); 37 | } 38 | 39 | criterion_group!(benches, std_arc_deref, std_arc_clone); 40 | criterion_main!(benches); 41 | -------------------------------------------------------------------------------- /benches/std_rc.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "fatal-warnings", deny(warnings))] 2 | 3 | use std::ops::Deref; 4 | use std::rc::Rc; 5 | 6 | use criterion::{Criterion, criterion_group, criterion_main}; 7 | use std::hint::black_box; 8 | 9 | fn std_rc_deref(c: &mut Criterion) { 10 | let limit = 200_000; 11 | 12 | c.bench_function("std rc deref", move |b| { 13 | b.iter(|| { 14 | let rc = Rc::new(42); 15 | 16 | for _ in 0..limit { 17 | black_box(rc.deref()); 18 | } 19 | 20 | rc 21 | }); 22 | }); 23 | } 24 | 25 | fn std_rc_clone_and_drop(c: &mut Criterion) { 26 | let limit = 100_000; 27 | 28 | c.bench_function("std rc clone and drop", move |b| { 29 | b.iter_with_setup( 30 | || Vec::with_capacity(limit), 31 | |mut vec| { 32 | vec.resize(limit, Rc::new(42)); 33 | vec 34 | }, 35 | ); 36 | }); 37 | } 38 | 39 | criterion_group!(benches, std_rc_deref, std_rc_clone_and_drop); 40 | criterion_main!(benches); 41 | -------------------------------------------------------------------------------- /examples/example.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_variables)] 3 | 4 | use archery::*; 5 | 6 | struct Image {} 7 | 8 | // The type parameter `P` will allow you to choose between an `Rc` or `Arc` pointer: 9 | struct Book { 10 | // A `SharedPointer` is a reference-counting pointer that can be atomic or non-atomic depending 11 | // on the second type argument: 12 | cover: SharedPointer, 13 | text: Vec, 14 | } 15 | 16 | impl Book

{ 17 | fn new(cover: Image, text: Vec) -> Book

{ 18 | Book { 19 | // Creates a shared pointer of kind `P`: 20 | cover: SharedPointer::new(cover), 21 | text, 22 | } 23 | } 24 | } 25 | 26 | fn main() { 27 | // To create a book that uses a non-atomic reference-counting pointer you specify the 28 | // `RcK` type argument. 29 | let book_rc = Book::::new(Image {}, Vec::new()); 30 | 31 | // Similarly, you can create an atomic reference-counting pointer with the 32 | // `ArcK` type argument. 33 | let book_arc = Book::::new(Image {}, Vec::new()); 34 | 35 | // `book_arc` will have a `cover` that is backed by an `Arc` pointer, thus implementing `Sync`: 36 | let _: Box = Box::new(book_arc); 37 | } 38 | -------------------------------------------------------------------------------- /images/archery.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 64 | 69 | 74 | rc 85 | 86 | 87 | -------------------------------------------------------------------------------- /release-notes.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | ## 1.2.2 4 | 5 | * Relicensed project under the MIT license. 6 | 7 | ## 1.2.1 8 | 9 | * No longer depend on `static_assertions`. 10 | 11 | ## 1.2.0 12 | 13 | * Added support for serde. This is gated behind the `serde` feature. 14 | 15 | ## 1.1.0 16 | 17 | * Added support for `triomphe::Arc` as a pointer kind. This gated behind the `triomphe` feature. 18 | 19 | ## 1.0.0 20 | 21 | * No changes. It’s just time to commit to a stable release :). 22 | 23 | ## 0.5.0 24 | 25 | * Fix `Send`/`Sync` unsoundness in `SharedPointer`. See issue [#18](https://github.com/orium/archery/issues/18). 26 | * Added `SharedPointer::pin()`. 27 | * Added `SharedPointer::as_ptr()`. 28 | * Updated dependencies. 29 | * Updated to 2021 edition. 30 | 31 | ## 0.4.0 32 | 33 | * Added support for `no_std`. 34 | 35 | ## 0.3.0 36 | 37 | * Renamed `SharedPointerKindRc` and `SharedPointerKindArc` to `RcK` and `ArcK`, respectively. 38 | 39 | ## 0.2.1 40 | 41 | * Minor fix in README. 42 | 43 | ## 0.2.0 44 | 45 | * Added some functionality to `SharedPointer` that you would expect from `Rc`/`Arc`. 46 | * Functions: 47 | * `SharedPointer::try_unwrap()`. 48 | * `SharedPointer::get_mut()`. 49 | * `SharedPointer::strong_count()`. 50 | * `SharedPointer::ptr_eq()`. 51 | * Traits: 52 | * `Default`. 53 | * `From`. 54 | * `From>`. 55 | * `std::fmt::Pointer`. 56 | 57 | ## 0.1.0 58 | 59 | * Initial release with `SharedPointer`, `SharedPointerKind`, `SharedPointerKindRc`, and `SharedPointerKindArc`. 60 | * Functionality exposed from the underlying pointers: `deref()`, `make_mut()`, `clone()`. 61 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![cfg_attr(feature = "fatal-warnings", deny(warnings))] 3 | // Note: If you change this remember to update `README.md`. To do so run `cargo rdme`. 4 | //! `archery` is a rust library that offers a way to abstraction over 5 | //! [`Rc`](::alloc::rc::Rc) and 6 | //! [`Arc`](::alloc::sync::Arc) smart pointers. 7 | //! This allows you to create data structures where the pointer type is parameterizable, so you can 8 | //! [avoid the overhead of `Arc`](::alloc::sync::Arc#thread-safety) 9 | //! when you don’t need to share data across threads. 10 | //! 11 | //! In languages that supports 12 | //! [higher-kinded polymorphism](https://en.wikipedia.org/wiki/Type_class#Higher-kinded_polymorphism) 13 | //! this would be simple to achieve without any library, but 14 | //! [rust does not support that yet](https://github.com/rust-lang/rfcs/issues/324). 15 | //! To mimic higher-kinded polymorphism `archery` implements the approach suggested by 16 | //! Joshua Liebow-Feeser in 17 | //! “[Rust has higher kinded types already… sort of](https://joshlf.com/post/2018/10/18/rust-higher-kinded-types-already/)”. 18 | //! While [other approaches](#alternative-approaches) exist, they seem to always offer poor 19 | //! ergonomics for the user. 20 | //! 21 | //! # Setup 22 | //! 23 | //! To use `archery` add the following to your `Cargo.toml`: 24 | //! 25 | //! ```toml 26 | //! [dependencies] 27 | //! archery = "" 28 | //! ``` 29 | //! 30 | //! # Using `archery` 31 | //! 32 | //! `archery` defines a [`SharedPointer`](crate::shared_pointer::SharedPointer) 33 | //! that receives the [kind of pointer](crate::shared_pointer::kind::SharedPointerKind) 34 | //! as a type parameter. This gives you a convenient and ergonomic way to abstract the pointer 35 | //! type away. 36 | //! 37 | //! ## Example 38 | //! 39 | //! Declare a data structure with the pointer kind as a type parameter bounded by 40 | //! [`SharedPointerKind`](crate::shared_pointer::kind::SharedPointerKind): 41 | //! 42 | //! ```rust 43 | //! use archery::*; 44 | //! 45 | //! struct KeyValuePair { 46 | //! pub key: SharedPointer, 47 | //! pub value: SharedPointer, 48 | //! } 49 | //! 50 | //! impl KeyValuePair { 51 | //! fn new(key: K, value: V) -> KeyValuePair { 52 | //! KeyValuePair { 53 | //! key: SharedPointer::new(key), 54 | //! value: SharedPointer::new(value), 55 | //! } 56 | //! } 57 | //! } 58 | //! ``` 59 | //! 60 | //! To use it just plug-in the kind of pointer you want: 61 | //! 62 | //! ```rust 63 | //! # use archery::*; 64 | //! # 65 | //! # struct KeyValuePair { 66 | //! # pub key: SharedPointer, 67 | //! # pub value: SharedPointer, 68 | //! # } 69 | //! # 70 | //! # impl KeyValuePair { 71 | //! # fn new(key: K, value: V) -> KeyValuePair { 72 | //! # KeyValuePair { 73 | //! # key: SharedPointer::new(key), 74 | //! # value: SharedPointer::new(value), 75 | //! # } 76 | //! # } 77 | //! # } 78 | //! # 79 | //! let pair: KeyValuePair<_, _, RcK> = 80 | //! KeyValuePair::new("António Variações", 1944); 81 | //! 82 | //! assert_eq!(*pair.value, 1944); 83 | //! ``` 84 | //! 85 | //! ## `triomphe::Arc` 86 | //! 87 | //! You can also use [`triomphe::Arc`](https://docs.rs/triomphe/latest/triomphe/struct.Arc.html) 88 | //! as the backing implementation of a [`SharedPointer`](crate::shared_pointer::SharedPointer). 89 | //! This is generally faster than [`std::sync::Arc`](::alloc::sync::Arc). 90 | //! Read [`triomphe`’s crate documentation](https://docs.rs/triomphe/latest/triomphe/) to learn more 91 | //! about it. 92 | //! 93 | //! To use it you need to enable the `triomphe` feature in `archery`. Use `ArcTK` as the pointer 94 | //! kind in [`SharedPointer`](crate::shared_pointer::SharedPointer). 95 | //! 96 | //! ## Serialization 97 | //! 98 | //! We support serialization through [serde](https://crates.io/crates/serde). To use it 99 | //! enable the `serde` feature. To do so change the archery dependency in your `Cargo.toml` to 100 | //! 101 | //! ```toml 102 | //! [dependencies] 103 | //! archery = { version = "", features = ["serde"] } 104 | //! ``` 105 | //! # Limitations 106 | //! 107 | //! Currently it is not possible to have unsized types inside a 108 | //! [`SharedPointer`](crate::shared_pointer::SharedPointer). As a workaround you can put the 109 | //! unsized type inside a [`Box`](::alloc::boxed::Box). 110 | //! 111 | //! # Alternative approaches 112 | //! 113 | //! An alternative to the approach taken by `archery` is to use traits with associated types to encode 114 | //! type-level functions. This has been suggested 115 | //! [multiple](https://github.com/orium/rpds/issues/7#issuecomment-362635901) 116 | //! [times](https://joshlf.com/post/2018/10/18/rust-higher-kinded-types-already/#comment-4160863400), 117 | //! but offers ugly ergonomics (see 118 | //! [here](https://github.com/Marwes/rpds/blob/e482d5abbaa6c876d7c624e497affe7299bbeece/src/sequence/vector/mod.rs#L153) 119 | //! and [here](https://github.com/Marwes/rpds/blob/e482d5abbaa6c876d7c624e497affe7299bbeece/src/sequence/vector/mod.rs#L249)). 120 | 121 | extern crate alloc; 122 | 123 | #[cfg(test)] 124 | #[macro_use] 125 | extern crate std; 126 | 127 | pub mod shared_pointer; 128 | 129 | pub use shared_pointer::SharedPointer; 130 | 131 | pub use shared_pointer::kind::SharedPointerKind; 132 | 133 | #[doc(no_inline)] 134 | pub use shared_pointer::kind::ArcK; 135 | #[cfg(feature = "triomphe")] 136 | #[doc(no_inline)] 137 | pub use shared_pointer::kind::ArcTK; 138 | #[doc(no_inline)] 139 | pub use shared_pointer::kind::RcK; 140 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/arc/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::shared_pointer::kind::SharedPointerKind; 2 | use alloc::boxed::Box; 3 | use alloc::sync::Arc; 4 | use core::fmt; 5 | use core::fmt::Debug; 6 | use core::fmt::Formatter; 7 | use core::mem; 8 | use core::mem::ManuallyDrop; 9 | use core::ops::Deref; 10 | use core::ops::DerefMut; 11 | use core::ptr; 12 | 13 | type UntypedArc = Arc<()>; 14 | 15 | /// [Type constructors](https://en.wikipedia.org/wiki/Type_constructor) for 16 | /// [`Arc`] pointers. 17 | pub struct ArcK { 18 | /// We use [`ManuallyDrop`] here, so that we can drop it explicitly as 19 | /// [`Arc`](alloc::sync::Arc). Not sure if it can be dropped as [`UntypedArc`], but it 20 | /// seems to be playing with fire (even more than we already are). 21 | inner: ManuallyDrop, 22 | } 23 | 24 | impl ArcK { 25 | #[inline(always)] 26 | fn new_from_inner(arc: Arc) -> ArcK { 27 | ArcK { inner: ManuallyDrop::new(unsafe { mem::transmute::, UntypedArc>(arc) }) } 28 | } 29 | 30 | #[inline(always)] 31 | unsafe fn take_inner(self) -> Arc { 32 | unsafe { 33 | let arc: UntypedArc = ManuallyDrop::into_inner(self.inner); 34 | 35 | mem::transmute(arc) 36 | } 37 | } 38 | 39 | #[inline(always)] 40 | unsafe fn as_inner_ref(&self) -> &Arc { 41 | unsafe { 42 | let arc_t: *const Arc = 43 | ptr::from_ref::(self.inner.deref()).cast::>(); 44 | 45 | // Static check to make sure we are not messing up the sizes. 46 | // This could happen if we allowed for `T` to be unsized, because it would need to be 47 | // represented as a wide pointer inside `Arc`. 48 | // TODO Use static_assertion when https://github.com/nvzqz/static-assertions-rs/issues/21 49 | // gets fixed 50 | let _ = mem::transmute::>; 51 | 52 | &*arc_t 53 | } 54 | } 55 | 56 | #[inline(always)] 57 | unsafe fn as_inner_mut(&mut self) -> &mut Arc { 58 | unsafe { 59 | let arc_t: *mut Arc = 60 | ptr::from_mut::(self.inner.deref_mut()).cast::>(); 61 | 62 | &mut *arc_t 63 | } 64 | } 65 | } 66 | 67 | unsafe impl SharedPointerKind for ArcK { 68 | #[inline(always)] 69 | fn new(v: T) -> ArcK { 70 | ArcK::new_from_inner(Arc::new(v)) 71 | } 72 | 73 | #[inline(always)] 74 | fn from_box(v: Box) -> ArcK { 75 | ArcK::new_from_inner::(Arc::from(v)) 76 | } 77 | 78 | #[inline(always)] 79 | unsafe fn as_ptr(&self) -> *const T { 80 | unsafe { Arc::as_ptr(self.as_inner_ref()) } 81 | } 82 | 83 | #[inline(always)] 84 | unsafe fn deref(&self) -> &T { 85 | unsafe { self.as_inner_ref::().as_ref() } 86 | } 87 | 88 | #[inline(always)] 89 | unsafe fn try_unwrap(self) -> Result { 90 | unsafe { Arc::try_unwrap(self.take_inner()).map_err(ArcK::new_from_inner) } 91 | } 92 | 93 | #[inline(always)] 94 | unsafe fn get_mut(&mut self) -> Option<&mut T> { 95 | unsafe { Arc::get_mut(self.as_inner_mut()) } 96 | } 97 | 98 | #[inline(always)] 99 | unsafe fn make_mut(&mut self) -> &mut T { 100 | unsafe { Arc::make_mut(self.as_inner_mut()) } 101 | } 102 | 103 | #[inline(always)] 104 | unsafe fn strong_count(&self) -> usize { 105 | unsafe { Arc::strong_count(self.as_inner_ref::()) } 106 | } 107 | 108 | #[inline(always)] 109 | unsafe fn clone(&self) -> ArcK { 110 | unsafe { ArcK { inner: ManuallyDrop::new(Arc::clone(self.as_inner_ref())) } } 111 | } 112 | 113 | #[inline(always)] 114 | unsafe fn drop(&mut self) { 115 | unsafe { 116 | ptr::drop_in_place::>(self.as_inner_mut()); 117 | } 118 | } 119 | } 120 | 121 | impl Debug for ArcK { 122 | #[inline(always)] 123 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 124 | f.write_str("ArcK") 125 | } 126 | } 127 | 128 | #[cfg(test)] 129 | mod test; 130 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/arc/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use pretty_assertions::assert_eq; 3 | use static_assertions::assert_impl_all; 4 | use std::cell::Cell; 5 | use std::string::ToString; 6 | 7 | type PointerKind = ArcK; 8 | 9 | assert_impl_all!(ArcK: Send, Sync); 10 | 11 | #[test] 12 | fn test_from_box_t() { 13 | let mut ptr = PointerKind::from_box(Box::new(42)); 14 | 15 | unsafe { 16 | assert_eq!(ptr.deref::(), &42); 17 | 18 | ptr.drop::(); 19 | } 20 | } 21 | 22 | #[test] 23 | fn test_as_ptr() { 24 | let mut x = PointerKind::new::<&'static str>("hello"); 25 | 26 | unsafe { 27 | let mut y = PointerKind::clone::<&'static str>(&x); 28 | let x_ptr: *const &'static str = PointerKind::as_ptr(&x); 29 | 30 | assert_eq!(x_ptr, PointerKind::as_ptr(&y)); 31 | assert_eq!(*x_ptr, "hello"); 32 | 33 | x.drop::<&'static str>(); 34 | y.drop::<&'static str>(); 35 | } 36 | } 37 | 38 | #[test] 39 | fn test_deref() { 40 | let mut ptr_42 = PointerKind::new::(42); 41 | let mut ptr_box_dyn_hello = PointerKind::new::>(Box::new("hello")); 42 | 43 | unsafe { 44 | assert_eq!(ptr_42.deref::(), &42); 45 | assert_eq!(ptr_box_dyn_hello.deref::>().to_string(), "hello"); 46 | 47 | ptr_42.drop::(); 48 | ptr_box_dyn_hello.drop::>(); 49 | } 50 | } 51 | 52 | #[test] 53 | fn test_try_unwrap() { 54 | let ptr = PointerKind::new::(42); 55 | 56 | unsafe { 57 | assert_eq!(ptr.try_unwrap::().unwrap(), 42); 58 | } 59 | 60 | let ptr = PointerKind::new::(42); 61 | 62 | unsafe { 63 | let ptr_clone = ptr.clone::(); 64 | 65 | let mut ptr_clone = ptr_clone.try_unwrap::().unwrap_err(); 66 | let mut ptr = ptr.try_unwrap::().unwrap_err(); 67 | 68 | assert_eq!(ptr.deref::(), &42); 69 | assert_eq!(ptr_clone.deref::(), &42); 70 | 71 | ptr.drop::(); 72 | ptr_clone.drop::(); 73 | } 74 | } 75 | 76 | #[test] 77 | fn test_get_mut() { 78 | let mut ptr = PointerKind::new::(42); 79 | 80 | unsafe { 81 | assert_eq!(ptr.deref::(), &42); 82 | 83 | *ptr.get_mut::().unwrap() += 1; 84 | 85 | assert_eq!(ptr.deref::(), &43); 86 | 87 | let mut ptr_clone = ptr.clone::(); 88 | 89 | assert_eq!(ptr.get_mut::(), None); 90 | assert_eq!(ptr_clone.get_mut::(), None); 91 | 92 | ptr.drop::(); 93 | 94 | *ptr_clone.get_mut::().unwrap() += 1; 95 | 96 | assert_eq!(ptr_clone.deref::(), &44); 97 | 98 | ptr_clone.drop::(); 99 | } 100 | } 101 | 102 | #[test] 103 | fn test_make_mut() { 104 | let mut ptr = PointerKind::new::(42); 105 | 106 | unsafe { 107 | assert_eq!(ptr.deref::(), &42); 108 | 109 | *ptr.make_mut::() += 1; 110 | 111 | assert_eq!(ptr.deref::(), &43); 112 | 113 | // Clone to force make_mut to clone the data. 114 | let mut ptr_clone = ptr.clone::(); 115 | 116 | assert_eq!(ptr_clone.deref::(), &43); 117 | 118 | *ptr_clone.make_mut::() += 1; 119 | 120 | assert_eq!(ptr.deref::(), &43); 121 | assert_eq!(ptr_clone.deref::(), &44); 122 | 123 | *ptr.make_mut::() *= 2; 124 | 125 | assert_eq!(ptr.deref::(), &(2 * 43)); 126 | assert_eq!(ptr_clone.deref::(), &44); 127 | 128 | ptr.drop::(); 129 | 130 | assert_eq!(ptr_clone.deref::(), &44); 131 | 132 | ptr_clone.drop::(); 133 | } 134 | } 135 | 136 | #[test] 137 | fn test_strong_count() { 138 | let mut ptr = PointerKind::new::(42); 139 | 140 | unsafe { 141 | assert_eq!(ptr.strong_count::(), 1); 142 | 143 | let mut ptr_clone = ptr.clone::(); 144 | 145 | assert_eq!(ptr.strong_count::(), 2); 146 | assert_eq!(ptr_clone.strong_count::(), 2); 147 | 148 | ptr.drop::(); 149 | 150 | assert_eq!(ptr_clone.strong_count::(), 1); 151 | 152 | ptr_clone.drop::(); 153 | } 154 | } 155 | 156 | #[test] 157 | fn test_clone() { 158 | let mut ptr = PointerKind::new::>(Cell::new(42)); 159 | 160 | unsafe { 161 | let mut ptr_clone = ptr.clone::>(); 162 | 163 | assert_eq!(ptr.deref::>().get(), 42); 164 | assert_eq!(ptr_clone.deref::>().get(), 42); 165 | 166 | ptr_clone.deref::>().set(3); 167 | 168 | assert_eq!(ptr.deref::>().get(), 3); 169 | assert_eq!(ptr_clone.deref::>().get(), 3); 170 | 171 | ptr.drop::>(); 172 | 173 | assert_eq!(ptr_clone.deref::>().get(), 3); 174 | 175 | ptr_clone.drop::>(); 176 | } 177 | } 178 | 179 | #[test] 180 | fn test_debug() { 181 | let mut ptr = PointerKind::new::(42); 182 | 183 | assert_eq!(format!("{:?}", ptr), "ArcK"); 184 | 185 | unsafe { 186 | ptr.drop::(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/arct/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::shared_pointer::kind::SharedPointerKind; 2 | use alloc::boxed::Box; 3 | use core::fmt; 4 | use core::fmt::Debug; 5 | use core::fmt::Formatter; 6 | use core::mem; 7 | use core::mem::ManuallyDrop; 8 | use core::ops::Deref; 9 | use core::ops::DerefMut; 10 | use core::ptr; 11 | use triomphe::Arc; 12 | 13 | type UntypedArc = Arc<()>; 14 | 15 | /// [Type constructors](https://en.wikipedia.org/wiki/Type_constructor) for 16 | /// [`triomphe::Arc`](triomphe::Arc) pointers. 17 | pub struct ArcTK { 18 | /// We use [`ManuallyDrop`] here, so that we can drop it explicitly as 19 | /// [`Arc`](triomphe::Arc). Not sure if it can be dropped as [`UntypedArc`], but it 20 | /// seems to be playing with fire (even more than we already are). 21 | inner: ManuallyDrop, 22 | } 23 | 24 | impl ArcTK { 25 | #[inline(always)] 26 | fn new_from_inner(arc: Arc) -> ArcTK { 27 | ArcTK { inner: ManuallyDrop::new(unsafe { mem::transmute::, UntypedArc>(arc) }) } 28 | } 29 | 30 | #[inline(always)] 31 | unsafe fn take_inner(self) -> Arc { 32 | let arc: UntypedArc = ManuallyDrop::into_inner(self.inner); 33 | 34 | unsafe { mem::transmute(arc) } 35 | } 36 | 37 | #[inline(always)] 38 | unsafe fn as_inner_ref(&self) -> &Arc { 39 | let arc_t: *const Arc = ptr::from_ref::(self.inner.deref()).cast::>(); 40 | 41 | // Static check to make sure we are not messing up the sizes. 42 | // This could happen if we allowed for `T` to be unsized, because it would need to be 43 | // represented as a wide pointer inside `Arc`. 44 | // TODO Use static_assertion when https://github.com/nvzqz/static-assertions-rs/issues/21 45 | // gets fixed 46 | let _ = mem::transmute::>; 47 | 48 | unsafe { &*arc_t } 49 | } 50 | 51 | #[inline(always)] 52 | unsafe fn as_inner_mut(&mut self) -> &mut Arc { 53 | let arc_t: *mut Arc = 54 | ptr::from_mut::(self.inner.deref_mut()).cast::>(); 55 | 56 | unsafe { &mut *arc_t } 57 | } 58 | } 59 | 60 | unsafe impl SharedPointerKind for ArcTK { 61 | #[inline(always)] 62 | fn new(v: T) -> ArcTK { 63 | ArcTK::new_from_inner(Arc::new(v)) 64 | } 65 | 66 | #[inline(always)] 67 | fn from_box(v: Box) -> ArcTK { 68 | ArcTK::new_from_inner::(Arc::from(v)) 69 | } 70 | 71 | #[inline(always)] 72 | unsafe fn as_ptr(&self) -> *const T { 73 | unsafe { Arc::as_ptr(self.as_inner_ref()) } 74 | } 75 | 76 | #[inline(always)] 77 | unsafe fn deref(&self) -> &T { 78 | unsafe { self.as_inner_ref::().as_ref() } 79 | } 80 | 81 | #[inline(always)] 82 | unsafe fn try_unwrap(self) -> Result { 83 | unsafe { Arc::try_unwrap(self.take_inner()).map_err(ArcTK::new_from_inner) } 84 | } 85 | 86 | #[inline(always)] 87 | unsafe fn get_mut(&mut self) -> Option<&mut T> { 88 | unsafe { Arc::get_mut(self.as_inner_mut()) } 89 | } 90 | 91 | #[inline(always)] 92 | unsafe fn make_mut(&mut self) -> &mut T { 93 | unsafe { Arc::make_mut(self.as_inner_mut()) } 94 | } 95 | 96 | #[inline(always)] 97 | unsafe fn strong_count(&self) -> usize { 98 | unsafe { Arc::count(self.as_inner_ref::()) } 99 | } 100 | 101 | #[inline(always)] 102 | unsafe fn clone(&self) -> ArcTK { 103 | unsafe { ArcTK { inner: ManuallyDrop::new(Arc::clone(self.as_inner_ref())) } } 104 | } 105 | 106 | #[inline(always)] 107 | unsafe fn drop(&mut self) { 108 | unsafe { 109 | ptr::drop_in_place::>(self.as_inner_mut()); 110 | } 111 | } 112 | } 113 | 114 | impl Debug for ArcTK { 115 | #[inline(always)] 116 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 117 | f.write_str("ArcTK") 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod test; 123 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/arct/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use pretty_assertions::assert_eq; 3 | use static_assertions::assert_impl_all; 4 | use std::cell::Cell; 5 | use std::string::ToString; 6 | 7 | type PointerKind = ArcTK; 8 | 9 | assert_impl_all!(ArcTK: Send, Sync); 10 | 11 | #[test] 12 | fn test_from_box_t() { 13 | let mut ptr = PointerKind::from_box(Box::new(42)); 14 | 15 | unsafe { 16 | assert_eq!(ptr.deref::(), &42); 17 | 18 | ptr.drop::(); 19 | } 20 | } 21 | 22 | #[test] 23 | fn test_as_ptr() { 24 | let mut x = PointerKind::new::<&'static str>("hello"); 25 | 26 | unsafe { 27 | let mut y = PointerKind::clone::<&'static str>(&x); 28 | let x_ptr: *const &'static str = PointerKind::as_ptr(&x); 29 | 30 | assert_eq!(x_ptr, PointerKind::as_ptr(&y)); 31 | assert_eq!(*x_ptr, "hello"); 32 | 33 | x.drop::<&'static str>(); 34 | y.drop::<&'static str>(); 35 | } 36 | } 37 | 38 | #[test] 39 | fn test_deref() { 40 | let mut ptr_42 = PointerKind::new::(42); 41 | let mut ptr_box_dyn_hello = PointerKind::new::>(Box::new("hello")); 42 | 43 | unsafe { 44 | assert_eq!(ptr_42.deref::(), &42); 45 | assert_eq!(ptr_box_dyn_hello.deref::>().to_string(), "hello"); 46 | 47 | ptr_42.drop::(); 48 | ptr_box_dyn_hello.drop::>(); 49 | } 50 | } 51 | 52 | #[test] 53 | fn test_try_unwrap() { 54 | let ptr = PointerKind::new::(42); 55 | 56 | unsafe { 57 | assert_eq!(ptr.try_unwrap::().unwrap(), 42); 58 | } 59 | 60 | let ptr = PointerKind::new::(42); 61 | 62 | unsafe { 63 | let ptr_clone = ptr.clone::(); 64 | 65 | let mut ptr_clone = ptr_clone.try_unwrap::().unwrap_err(); 66 | let mut ptr = ptr.try_unwrap::().unwrap_err(); 67 | 68 | assert_eq!(ptr.deref::(), &42); 69 | assert_eq!(ptr_clone.deref::(), &42); 70 | 71 | ptr.drop::(); 72 | ptr_clone.drop::(); 73 | } 74 | } 75 | 76 | #[test] 77 | fn test_get_mut() { 78 | let mut ptr = PointerKind::new::(42); 79 | 80 | unsafe { 81 | assert_eq!(ptr.deref::(), &42); 82 | 83 | *ptr.get_mut::().unwrap() += 1; 84 | 85 | assert_eq!(ptr.deref::(), &43); 86 | 87 | let mut ptr_clone = ptr.clone::(); 88 | 89 | assert_eq!(ptr.get_mut::(), None); 90 | assert_eq!(ptr_clone.get_mut::(), None); 91 | 92 | ptr.drop::(); 93 | 94 | *ptr_clone.get_mut::().unwrap() += 1; 95 | 96 | assert_eq!(ptr_clone.deref::(), &44); 97 | 98 | ptr_clone.drop::(); 99 | } 100 | } 101 | 102 | #[test] 103 | fn test_make_mut() { 104 | let mut ptr = PointerKind::new::(42); 105 | 106 | unsafe { 107 | assert_eq!(ptr.deref::(), &42); 108 | 109 | *ptr.make_mut::() += 1; 110 | 111 | assert_eq!(ptr.deref::(), &43); 112 | 113 | // Clone to force make_mut to clone the data. 114 | let mut ptr_clone = ptr.clone::(); 115 | 116 | assert_eq!(ptr_clone.deref::(), &43); 117 | 118 | *ptr_clone.make_mut::() += 1; 119 | 120 | assert_eq!(ptr.deref::(), &43); 121 | assert_eq!(ptr_clone.deref::(), &44); 122 | 123 | *ptr.make_mut::() *= 2; 124 | 125 | assert_eq!(ptr.deref::(), &(2 * 43)); 126 | assert_eq!(ptr_clone.deref::(), &44); 127 | 128 | ptr.drop::(); 129 | 130 | assert_eq!(ptr_clone.deref::(), &44); 131 | 132 | ptr_clone.drop::(); 133 | } 134 | } 135 | 136 | #[test] 137 | fn test_strong_count() { 138 | let mut ptr = PointerKind::new::(42); 139 | 140 | unsafe { 141 | assert_eq!(ptr.strong_count::(), 1); 142 | 143 | let mut ptr_clone = ptr.clone::(); 144 | 145 | assert_eq!(ptr.strong_count::(), 2); 146 | assert_eq!(ptr_clone.strong_count::(), 2); 147 | 148 | ptr.drop::(); 149 | 150 | assert_eq!(ptr_clone.strong_count::(), 1); 151 | 152 | ptr_clone.drop::(); 153 | } 154 | } 155 | 156 | #[test] 157 | fn test_clone() { 158 | let mut ptr = PointerKind::new::>(Cell::new(42)); 159 | 160 | unsafe { 161 | let mut ptr_clone = ptr.clone::>(); 162 | 163 | assert_eq!(ptr.deref::>().get(), 42); 164 | assert_eq!(ptr_clone.deref::>().get(), 42); 165 | 166 | ptr_clone.deref::>().set(3); 167 | 168 | assert_eq!(ptr.deref::>().get(), 3); 169 | assert_eq!(ptr_clone.deref::>().get(), 3); 170 | 171 | ptr.drop::>(); 172 | 173 | assert_eq!(ptr_clone.deref::>().get(), 3); 174 | 175 | ptr_clone.drop::>(); 176 | } 177 | } 178 | 179 | #[test] 180 | fn test_debug() { 181 | let mut ptr = PointerKind::new::(42); 182 | 183 | assert_eq!(format!("{:?}", ptr), "ArcTK"); 184 | 185 | unsafe { 186 | ptr.drop::(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/mod.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | /// Trait for [type constructors](https://en.wikipedia.org/wiki/Type_constructor) of 4 | /// reference-counting pointers. 5 | /// 6 | /// # Safety 7 | /// 8 | /// `T` may be `!`[`Unpin`], and [`SharedPointer`][1] may be held in a pinned 9 | /// form ([`Pin`][2]`>`). 10 | /// As such, the implementation of this trait must uphold the pinning invariants 11 | /// for `T` while it's held in `Self`. Specifically, this necessitates the 12 | /// following: 13 | /// 14 | /// - `&mut T` is only exposed through the trait methods returning `&mut T`. 15 | /// 16 | /// - The implementor must not move out the contained `T` unless the semantics 17 | /// of trait methods demands that. 18 | /// 19 | /// - [`Self::drop`] drops `T` in place. 20 | /// 21 | /// [1]: crate::shared_pointer::SharedPointer 22 | /// [2]: core::pin::Pin 23 | // 24 | // There are two conditions for types implementing this trait to be used in a safe way: 25 | // 26 | // 1. Always use the correct type `T`. 27 | // 2. Make sure that you use it wrapped in something that derives the correct auto-traits taking 28 | // into account the type of `T`. 29 | // 30 | // To elaborate on point 2: a `ArcK` will always implement `Send + Sync`, but that 31 | // is only safe if the actually type that `ArcK` holds is in fact `Send + Sync`. 32 | // This means that a safe wrapper around this type must make sure it does not implement 33 | // `Send + Sync` unless `T: Send + Sync`. This is holds true for `SharedPointer` since it has a 34 | // phantom field with `T`, thus the compiler will only make `SharedPointer` implement 35 | // `Send + Sync` if `T: Send + Sync`. 36 | pub unsafe trait SharedPointerKind: Sized + Debug { 37 | fn new(v: T) -> Self; 38 | fn from_box(v: Box) -> Self; 39 | unsafe fn as_ptr(&self) -> *const T; 40 | unsafe fn deref(&self) -> &T; 41 | unsafe fn try_unwrap(self) -> Result; 42 | unsafe fn get_mut(&mut self) -> Option<&mut T>; 43 | unsafe fn make_mut(&mut self) -> &mut T; 44 | unsafe fn strong_count(&self) -> usize; 45 | #[must_use] 46 | unsafe fn clone(&self) -> Self; 47 | unsafe fn drop(&mut self); 48 | } 49 | 50 | mod arc; 51 | #[cfg(feature = "triomphe")] 52 | mod arct; 53 | mod rc; 54 | 55 | use alloc::boxed::Box; 56 | #[doc(inline)] 57 | pub use arc::ArcK; 58 | #[cfg(feature = "triomphe")] 59 | #[doc(inline)] 60 | pub use arct::ArcTK; 61 | #[doc(inline)] 62 | pub use rc::RcK; 63 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/rc/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::shared_pointer::kind::SharedPointerKind; 2 | use alloc::boxed::Box; 3 | use alloc::rc::Rc; 4 | use core::fmt; 5 | use core::fmt::Debug; 6 | use core::fmt::Formatter; 7 | use core::mem; 8 | use core::mem::ManuallyDrop; 9 | use core::ops::Deref; 10 | use core::ops::DerefMut; 11 | use core::ptr; 12 | 13 | type UntypedRc = Rc<()>; 14 | 15 | /// [Type constructors](https://en.wikipedia.org/wiki/Type_constructor) for 16 | /// [`Rc`] pointers. 17 | pub struct RcK { 18 | /// We use [`ManuallyDrop`] here, so that we can drop it explicitly as [`Rc`](alloc::rc::Rc). 19 | /// Not sure if it can be dropped as [`UntypedRc`], but it seems to be playing with fire (even 20 | /// more than we already are). 21 | inner: ManuallyDrop, 22 | } 23 | 24 | impl RcK { 25 | #[inline(always)] 26 | fn new_from_inner(rc: Rc) -> RcK { 27 | RcK { inner: ManuallyDrop::new(unsafe { mem::transmute::, UntypedRc>(rc) }) } 28 | } 29 | 30 | #[inline(always)] 31 | unsafe fn take_inner(self) -> Rc { 32 | unsafe { 33 | let rc: UntypedRc = ManuallyDrop::into_inner(self.inner); 34 | 35 | mem::transmute(rc) 36 | } 37 | } 38 | 39 | #[inline(always)] 40 | unsafe fn as_inner_ref(&self) -> &Rc { 41 | unsafe { 42 | let rc_t: *const Rc = ptr::from_ref::(self.inner.deref()).cast::>(); 43 | 44 | // Static check to make sure we are not messing up the sizes. 45 | // This could happen if we allowed for `T` to be unsized, because it would need to be 46 | // represented as a wide pointer inside `Rc`. 47 | // TODO Use static_assertion when https://github.com/nvzqz/static-assertions-rs/issues/21 48 | // gets fixed 49 | let _ = mem::transmute::>; 50 | 51 | &*rc_t 52 | } 53 | } 54 | 55 | #[inline(always)] 56 | unsafe fn as_inner_mut(&mut self) -> &mut Rc { 57 | unsafe { 58 | let rc_t: *mut Rc = 59 | ptr::from_mut::(self.inner.deref_mut()).cast::>(); 60 | 61 | &mut *rc_t 62 | } 63 | } 64 | } 65 | 66 | unsafe impl SharedPointerKind for RcK { 67 | #[inline(always)] 68 | fn new(v: T) -> RcK { 69 | RcK::new_from_inner(Rc::new(v)) 70 | } 71 | 72 | #[inline(always)] 73 | fn from_box(v: Box) -> RcK { 74 | RcK::new_from_inner::(Rc::from(v)) 75 | } 76 | 77 | #[inline(always)] 78 | unsafe fn as_ptr(&self) -> *const T { 79 | unsafe { Rc::as_ptr(self.as_inner_ref()) } 80 | } 81 | 82 | #[inline(always)] 83 | unsafe fn deref(&self) -> &T { 84 | unsafe { self.as_inner_ref::().as_ref() } 85 | } 86 | 87 | #[inline(always)] 88 | unsafe fn try_unwrap(self) -> Result { 89 | unsafe { Rc::try_unwrap(self.take_inner()).map_err(RcK::new_from_inner) } 90 | } 91 | 92 | #[inline(always)] 93 | unsafe fn get_mut(&mut self) -> Option<&mut T> { 94 | unsafe { Rc::get_mut(self.as_inner_mut()) } 95 | } 96 | 97 | #[inline(always)] 98 | unsafe fn make_mut(&mut self) -> &mut T { 99 | unsafe { Rc::make_mut(self.as_inner_mut()) } 100 | } 101 | 102 | #[inline(always)] 103 | unsafe fn strong_count(&self) -> usize { 104 | unsafe { Rc::strong_count(self.as_inner_ref::()) } 105 | } 106 | 107 | #[inline(always)] 108 | unsafe fn clone(&self) -> RcK { 109 | unsafe { RcK { inner: ManuallyDrop::new(Rc::clone(self.as_inner_ref())) } } 110 | } 111 | 112 | #[inline(always)] 113 | unsafe fn drop(&mut self) { 114 | unsafe { 115 | ptr::drop_in_place::>(self.as_inner_mut()); 116 | } 117 | } 118 | } 119 | 120 | impl Debug for RcK { 121 | #[inline(always)] 122 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 123 | f.write_str("RcK") 124 | } 125 | } 126 | 127 | #[cfg(test)] 128 | mod test; 129 | -------------------------------------------------------------------------------- /src/shared_pointer/kind/rc/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::cell::Cell; 3 | use std::string::ToString; 4 | 5 | type PointerKind = RcK; 6 | 7 | #[test] 8 | fn test_from_box_t() { 9 | let mut ptr = PointerKind::from_box(Box::new(42)); 10 | 11 | unsafe { 12 | assert_eq!(ptr.deref::(), &42); 13 | 14 | ptr.drop::(); 15 | } 16 | } 17 | 18 | #[test] 19 | fn test_as_ptr() { 20 | let mut x = PointerKind::new::<&'static str>("hello from test_as_ptr"); 21 | 22 | unsafe { 23 | let mut y = PointerKind::clone::<&'static str>(&x); 24 | let x_ptr: *const &'static str = PointerKind::as_ptr(&x); 25 | 26 | assert_eq!(x_ptr, PointerKind::as_ptr(&y)); 27 | assert_eq!(*x_ptr, "hello from test_as_ptr"); 28 | 29 | x.drop::<&'static str>(); 30 | y.drop::<&'static str>(); 31 | } 32 | } 33 | 34 | #[test] 35 | fn test_deref() { 36 | let mut ptr_42 = PointerKind::new::(42); 37 | let mut ptr_box_dyn_hello = 38 | PointerKind::new::>(Box::new("hello from test_deref")); 39 | 40 | unsafe { 41 | assert_eq!(ptr_42.deref::(), &42); 42 | assert_eq!( 43 | ptr_box_dyn_hello.deref::>().to_string(), 44 | "hello from test_deref" 45 | ); 46 | 47 | ptr_42.drop::(); 48 | ptr_box_dyn_hello.drop::>(); 49 | } 50 | } 51 | 52 | #[test] 53 | fn test_try_unwrap() { 54 | let ptr = PointerKind::new::(42); 55 | 56 | unsafe { 57 | assert_eq!(ptr.try_unwrap::().unwrap(), 42); 58 | } 59 | 60 | let ptr = PointerKind::new::(42); 61 | 62 | unsafe { 63 | let ptr_clone = ptr.clone::(); 64 | 65 | let mut ptr_clone = ptr_clone.try_unwrap::().unwrap_err(); 66 | let mut ptr = ptr.try_unwrap::().unwrap_err(); 67 | 68 | assert_eq!(ptr.deref::(), &42); 69 | assert_eq!(ptr_clone.deref::(), &42); 70 | 71 | ptr.drop::(); 72 | ptr_clone.drop::(); 73 | } 74 | } 75 | 76 | #[test] 77 | fn test_get_mut() { 78 | let mut ptr = PointerKind::new::(42); 79 | 80 | unsafe { 81 | assert_eq!(ptr.deref::(), &42); 82 | 83 | *ptr.get_mut::().unwrap() += 1; 84 | 85 | assert_eq!(ptr.deref::(), &43); 86 | 87 | let mut ptr_clone = ptr.clone::(); 88 | 89 | assert_eq!(ptr.get_mut::(), None); 90 | assert_eq!(ptr_clone.get_mut::(), None); 91 | 92 | ptr.drop::(); 93 | 94 | *ptr_clone.get_mut::().unwrap() += 1; 95 | 96 | assert_eq!(ptr_clone.deref::(), &44); 97 | 98 | ptr_clone.drop::(); 99 | } 100 | } 101 | 102 | #[test] 103 | fn test_make_mut() { 104 | let mut ptr = PointerKind::new::(42); 105 | 106 | unsafe { 107 | assert_eq!(ptr.deref::(), &42); 108 | 109 | *ptr.make_mut::() += 1; 110 | 111 | assert_eq!(ptr.deref::(), &43); 112 | 113 | // Clone to force make_mut to clone the data. 114 | let mut ptr_clone = ptr.clone::(); 115 | 116 | assert_eq!(ptr_clone.deref::(), &43); 117 | 118 | *ptr_clone.make_mut::() += 1; 119 | 120 | assert_eq!(ptr.deref::(), &43); 121 | assert_eq!(ptr_clone.deref::(), &44); 122 | 123 | *ptr.make_mut::() *= 2; 124 | 125 | assert_eq!(ptr.deref::(), &(2 * 43)); 126 | assert_eq!(ptr_clone.deref::(), &44); 127 | 128 | ptr.drop::(); 129 | 130 | assert_eq!(ptr_clone.deref::(), &44); 131 | 132 | ptr_clone.drop::(); 133 | } 134 | } 135 | 136 | #[test] 137 | fn test_strong_count() { 138 | let mut ptr = PointerKind::new::(42); 139 | 140 | unsafe { 141 | assert_eq!(ptr.strong_count::(), 1); 142 | 143 | let mut ptr_clone = ptr.clone::(); 144 | 145 | assert_eq!(ptr.strong_count::(), 2); 146 | assert_eq!(ptr_clone.strong_count::(), 2); 147 | 148 | ptr.drop::(); 149 | 150 | assert_eq!(ptr_clone.strong_count::(), 1); 151 | 152 | ptr_clone.drop::(); 153 | } 154 | } 155 | 156 | #[test] 157 | fn test_clone() { 158 | let mut ptr = PointerKind::new::>(Cell::new(42)); 159 | 160 | unsafe { 161 | let mut ptr_clone = ptr.clone::>(); 162 | 163 | assert_eq!(ptr.deref::>().get(), 42); 164 | assert_eq!(ptr_clone.deref::>().get(), 42); 165 | 166 | ptr_clone.deref::>().set(3); 167 | 168 | assert_eq!(ptr.deref::>().get(), 3); 169 | assert_eq!(ptr_clone.deref::>().get(), 3); 170 | 171 | ptr.drop::>(); 172 | 173 | assert_eq!(ptr_clone.deref::>().get(), 3); 174 | 175 | ptr_clone.drop::>(); 176 | } 177 | } 178 | 179 | #[test] 180 | fn test_debug() { 181 | let mut ptr = PointerKind::new::(42); 182 | 183 | assert_eq!(format!("{ptr:?}"), "RcK"); 184 | 185 | unsafe { 186 | ptr.drop::(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/shared_pointer/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::shared_pointer::kind::SharedPointerKind; 2 | use alloc::boxed::Box; 3 | use core::borrow::Borrow; 4 | use core::cmp::Ordering; 5 | use core::fmt; 6 | use core::fmt::Debug; 7 | use core::fmt::Display; 8 | use core::fmt::Formatter; 9 | use core::hash::Hash; 10 | use core::hash::Hasher; 11 | use core::marker::PhantomData; 12 | use core::mem; 13 | use core::mem::ManuallyDrop; 14 | use core::ops::Deref; 15 | use core::pin::Pin; 16 | use core::ptr; 17 | 18 | /// Pointer to shared data with reference-counting. 19 | /// 20 | /// The type parameter `P` is a [type constructor](https://en.wikipedia.org/wiki/Type_constructor) 21 | /// of the underlying pointer type, offering a way to abstraction over [`Rc`](alloc::rc::Rc) and 22 | /// [`Arc`](alloc::sync::Arc) smart pointers. 23 | /// This allows you to create data structures where the pointer type is parameterizable, so you can 24 | /// [avoid the overhead of `Arc`](alloc::sync::Arc#thread-safety) 25 | /// when you don’t need to share data across threads. 26 | /// 27 | /// # Example 28 | /// 29 | /// Declare a data structure with the pointer kind as a type parameter bounded by 30 | /// [`SharedPointerKind`]: 31 | /// 32 | /// ```rust 33 | /// use archery::*; 34 | /// 35 | /// struct KeyValuePair { 36 | /// pub key: SharedPointer, 37 | /// pub value: SharedPointer, 38 | /// } 39 | /// 40 | /// impl KeyValuePair { 41 | /// fn new(key: K, value: V) -> KeyValuePair { 42 | /// KeyValuePair { 43 | /// key: SharedPointer::new(key), 44 | /// value: SharedPointer::new(value), 45 | /// } 46 | /// } 47 | /// } 48 | /// ``` 49 | /// 50 | /// To use it just plug-in the kind of pointer you want: 51 | /// 52 | /// ```rust 53 | /// # use archery::*; 54 | /// # 55 | /// # struct KeyValuePair { 56 | /// # pub key: SharedPointer, 57 | /// # pub value: SharedPointer, 58 | /// # } 59 | /// # 60 | /// # impl KeyValuePair { 61 | /// # fn new(key: K, value: V) -> KeyValuePair { 62 | /// # KeyValuePair { 63 | /// # key: SharedPointer::new(key), 64 | /// # value: SharedPointer::new(value), 65 | /// # } 66 | /// # } 67 | /// # } 68 | /// # 69 | /// let pair: KeyValuePair<_, _, RcK> = 70 | /// KeyValuePair::new("António Variações", 1944); 71 | /// 72 | /// assert_eq!(*pair.value, 1944); 73 | /// ``` 74 | pub struct SharedPointer 75 | where 76 | P: SharedPointerKind, 77 | { 78 | ptr: ManuallyDrop

, 79 | _phantom_t: PhantomData, 80 | _phantom_no_send_sync: PhantomData<*mut ()>, 81 | } 82 | 83 | unsafe impl Send for SharedPointer where P: SharedPointerKind {} 84 | unsafe impl Sync for SharedPointer where P: SharedPointerKind {} 85 | 86 | impl Unpin for SharedPointer where P: SharedPointerKind {} 87 | 88 | impl SharedPointer 89 | where 90 | P: SharedPointerKind, 91 | { 92 | #[inline(always)] 93 | fn new_from_inner(ptr: P) -> SharedPointer { 94 | SharedPointer { 95 | ptr: ManuallyDrop::new(ptr), 96 | _phantom_t: PhantomData, 97 | _phantom_no_send_sync: PhantomData, 98 | } 99 | } 100 | 101 | #[inline(always)] 102 | pub fn new(v: T) -> SharedPointer { 103 | SharedPointer::new_from_inner(P::new::(v)) 104 | } 105 | 106 | #[inline(always)] 107 | pub fn pin(v: T) -> Pin> { 108 | unsafe { Pin::new_unchecked(Self::new(v)) } 109 | } 110 | 111 | #[inline(always)] 112 | pub fn as_ptr(this: &Self) -> *const T { 113 | unsafe { this.ptr.as_ptr::() } 114 | } 115 | 116 | #[inline(always)] 117 | pub fn try_unwrap(mut this: SharedPointer) -> Result> { 118 | let ptr: P = unsafe { ManuallyDrop::take(&mut this.ptr) }; 119 | 120 | mem::forget(this); 121 | 122 | unsafe { ptr.try_unwrap::() }.map_err(SharedPointer::new_from_inner) 123 | } 124 | 125 | #[inline(always)] 126 | pub fn get_mut(this: &mut SharedPointer) -> Option<&mut T> { 127 | unsafe { this.ptr.get_mut::() } 128 | } 129 | 130 | #[inline(always)] 131 | pub fn strong_count(this: &Self) -> usize { 132 | unsafe { this.ptr.strong_count::() } 133 | } 134 | 135 | #[inline(always)] 136 | pub fn ptr_eq( 137 | this: &SharedPointer, 138 | other: &SharedPointer, 139 | ) -> bool { 140 | ptr::eq(this.deref(), other.deref()) 141 | } 142 | } 143 | 144 | impl SharedPointer 145 | where 146 | T: Clone, 147 | P: SharedPointerKind, 148 | { 149 | #[inline(always)] 150 | pub fn make_mut(this: &mut SharedPointer) -> &mut T { 151 | unsafe { this.ptr.make_mut::() } 152 | } 153 | } 154 | 155 | impl Default for SharedPointer 156 | where 157 | T: Default, 158 | P: SharedPointerKind, 159 | { 160 | #[inline(always)] 161 | fn default() -> SharedPointer { 162 | SharedPointer::new(Default::default()) 163 | } 164 | } 165 | 166 | impl Deref for SharedPointer 167 | where 168 | P: SharedPointerKind, 169 | { 170 | type Target = T; 171 | 172 | #[inline(always)] 173 | fn deref(&self) -> &T { 174 | unsafe { self.ptr.deref().deref() } 175 | } 176 | } 177 | 178 | impl Borrow for SharedPointer 179 | where 180 | P: SharedPointerKind, 181 | { 182 | #[inline(always)] 183 | fn borrow(&self) -> &T { 184 | self.deref() 185 | } 186 | } 187 | 188 | impl AsRef for SharedPointer 189 | where 190 | P: SharedPointerKind, 191 | { 192 | #[inline(always)] 193 | fn as_ref(&self) -> &T { 194 | self.deref() 195 | } 196 | } 197 | 198 | impl Clone for SharedPointer 199 | where 200 | P: SharedPointerKind, 201 | { 202 | #[inline(always)] 203 | fn clone(&self) -> SharedPointer { 204 | SharedPointer::new_from_inner(unsafe { self.ptr.deref().clone::() }) 205 | } 206 | } 207 | 208 | impl Hash for SharedPointer 209 | where 210 | T: Hash, 211 | P: SharedPointerKind, 212 | { 213 | #[inline(always)] 214 | fn hash(&self, state: &mut H) { 215 | self.deref().hash(state); 216 | } 217 | } 218 | 219 | impl PartialEq> for SharedPointer 220 | where 221 | T: PartialEq, 222 | P: SharedPointerKind, 223 | PO: SharedPointerKind, 224 | { 225 | #[inline(always)] 226 | fn eq(&self, other: &SharedPointer) -> bool { 227 | self.deref().eq(other.deref()) 228 | } 229 | 230 | #[inline(always)] 231 | fn ne(&self, other: &SharedPointer) -> bool { 232 | self.deref().ne(other.deref()) 233 | } 234 | } 235 | 236 | impl Eq for SharedPointer 237 | where 238 | T: Eq, 239 | P: SharedPointerKind, 240 | { 241 | } 242 | 243 | impl PartialOrd> for SharedPointer 244 | where 245 | T: PartialOrd, 246 | P: SharedPointerKind, 247 | PO: SharedPointerKind, 248 | { 249 | #[inline(always)] 250 | fn partial_cmp(&self, other: &SharedPointer) -> Option { 251 | self.deref().partial_cmp(other.deref()) 252 | } 253 | 254 | #[inline(always)] 255 | fn lt(&self, other: &SharedPointer) -> bool { 256 | self.deref().lt(other.deref()) 257 | } 258 | 259 | #[inline(always)] 260 | fn le(&self, other: &SharedPointer) -> bool { 261 | self.deref().le(other.deref()) 262 | } 263 | 264 | #[inline(always)] 265 | fn gt(&self, other: &SharedPointer) -> bool { 266 | self.deref().gt(other.deref()) 267 | } 268 | 269 | #[inline(always)] 270 | fn ge(&self, other: &SharedPointer) -> bool { 271 | self.deref().ge(other.deref()) 272 | } 273 | } 274 | 275 | impl Ord for SharedPointer 276 | where 277 | T: Ord, 278 | P: SharedPointerKind, 279 | { 280 | #[inline(always)] 281 | fn cmp(&self, other: &SharedPointer) -> Ordering { 282 | self.deref().cmp(other.deref()) 283 | } 284 | } 285 | 286 | impl From for SharedPointer 287 | where 288 | P: SharedPointerKind, 289 | { 290 | #[inline(always)] 291 | fn from(other: T) -> SharedPointer { 292 | SharedPointer::new(other) 293 | } 294 | } 295 | 296 | impl From> for SharedPointer 297 | where 298 | P: SharedPointerKind, 299 | { 300 | #[inline(always)] 301 | fn from(v: Box) -> SharedPointer { 302 | SharedPointer::new_from_inner(P::from_box(v)) 303 | } 304 | } 305 | 306 | impl Debug for SharedPointer 307 | where 308 | T: Debug, 309 | P: SharedPointerKind, 310 | { 311 | #[inline(always)] 312 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 313 | Debug::fmt(self.deref(), f) 314 | } 315 | } 316 | 317 | impl fmt::Pointer for SharedPointer 318 | where 319 | P: SharedPointerKind, 320 | { 321 | #[inline(always)] 322 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 323 | fmt::Pointer::fmt(&core::ptr::addr_of!(**self), f) 324 | } 325 | } 326 | 327 | impl Display for SharedPointer 328 | where 329 | T: Display, 330 | P: SharedPointerKind, 331 | { 332 | #[inline(always)] 333 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 334 | Display::fmt(self.deref(), f) 335 | } 336 | } 337 | 338 | impl Drop for SharedPointer 339 | where 340 | P: SharedPointerKind, 341 | { 342 | #[inline(always)] 343 | fn drop(&mut self) { 344 | unsafe { 345 | self.ptr.drop::(); 346 | } 347 | } 348 | } 349 | 350 | pub mod kind; 351 | 352 | #[cfg(feature = "serde")] 353 | pub mod serde { 354 | use super::*; 355 | use ::serde::de::{Deserialize, Deserializer}; 356 | use ::serde::ser::{Serialize, Serializer}; 357 | 358 | impl Serialize for SharedPointer 359 | where 360 | T: Serialize, 361 | P: SharedPointerKind, 362 | { 363 | fn serialize(&self, serializer: S) -> Result { 364 | self.as_ref().serialize(serializer) 365 | } 366 | } 367 | 368 | impl<'de, T, P> Deserialize<'de> for SharedPointer 369 | where 370 | T: Deserialize<'de>, 371 | P: SharedPointerKind, 372 | { 373 | fn deserialize>( 374 | deserializer: D, 375 | ) -> Result, D::Error> { 376 | T::deserialize(deserializer).map(SharedPointer::new) 377 | } 378 | } 379 | } 380 | 381 | #[cfg(test)] 382 | mod test; 383 | -------------------------------------------------------------------------------- /src/shared_pointer/test.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::eq_op)] 2 | 3 | use super::*; 4 | use kind::ArcK; 5 | use kind::RcK; 6 | use static_assertions::assert_impl_all; 7 | use std::cell::Cell; 8 | use std::mem; 9 | use std::string::ToString; 10 | 11 | assert_impl_all!(SharedPointer: Send, Sync); 12 | 13 | mod static_check_rc_is_not_send_nor_sync { 14 | use crate::{RcK, SharedPointer}; 15 | use static_assertions::*; 16 | 17 | assert_impl_all!(i32: Send, Sync); 18 | 19 | assert_not_impl_any!(SharedPointer: Send); 20 | assert_not_impl_any!(SharedPointer: Sync); 21 | } 22 | 23 | mod static_check_arc_of_non_send_is_not_send_nor_sync { 24 | use crate::{ArcK, SharedPointer}; 25 | use static_assertions::*; 26 | use std::sync::MutexGuard; 27 | 28 | assert_not_impl_any!(MutexGuard<'static, ()>: Send); 29 | assert_impl_all!(MutexGuard<'static, ()>: Sync); 30 | 31 | assert_not_impl_any!(SharedPointer, ArcK>: Send); 32 | assert_not_impl_any!(SharedPointer, ArcK>: Sync); 33 | } 34 | 35 | mod static_check_arc_of_non_sync_is_not_send_nor_sync { 36 | use crate::{ArcK, SharedPointer}; 37 | use static_assertions::*; 38 | use std::cell::Cell; 39 | 40 | assert_impl_all!(Cell<()>: Send); 41 | assert_not_impl_any!(Cell<()>: Sync); 42 | 43 | assert_not_impl_any!(SharedPointer, ArcK>: Send); 44 | assert_not_impl_any!(SharedPointer, ArcK>: Sync); 45 | } 46 | 47 | mod static_check_arc_of_non_send_nor_sync_is_not_send_nor_sync { 48 | use crate::{ArcK, SharedPointer}; 49 | use alloc::rc::Rc; 50 | use static_assertions::*; 51 | 52 | assert_not_impl_any!(Rc: Send); 53 | assert_not_impl_any!(Rc: Sync); 54 | 55 | assert_not_impl_any!(SharedPointer, ArcK>: Send); 56 | assert_not_impl_any!(SharedPointer, ArcK>: Sync); 57 | } 58 | 59 | #[test] 60 | fn test_as_ptr() { 61 | let x = SharedPointer::<&'static str, RcK>::new("hello"); 62 | let y = SharedPointer::clone(&x); 63 | let x_ptr: *const &'static str = SharedPointer::as_ptr(&x); 64 | 65 | assert_eq!(x_ptr, SharedPointer::as_ptr(&y)); 66 | assert_eq!(unsafe { *x_ptr }, "hello"); 67 | } 68 | 69 | #[test] 70 | fn test_deref() { 71 | let ptr_42: SharedPointer = SharedPointer::new(42); 72 | let ptr_box_dyn_hello: SharedPointer, RcK> = 73 | SharedPointer::new(Box::new("hello")); 74 | 75 | assert_eq!(*ptr_42, 42); 76 | assert_eq!(ptr_box_dyn_hello.to_string(), "hello"); 77 | 78 | assert_eq!(*Borrow::::borrow(&ptr_42), 42); 79 | assert_eq!(Borrow::>::borrow(&ptr_box_dyn_hello).to_string(), "hello"); 80 | 81 | assert_eq!(*ptr_42.as_ref(), 42); 82 | assert_eq!(ptr_box_dyn_hello.as_ref().to_string(), "hello"); 83 | } 84 | 85 | #[test] 86 | fn test_try_unwrap() { 87 | let ptr: SharedPointer<_, RcK> = SharedPointer::new(42); 88 | 89 | assert_eq!(SharedPointer::try_unwrap(ptr).unwrap(), 42); 90 | 91 | let ptr: SharedPointer<_, RcK> = SharedPointer::new(42); 92 | let ptr_clone = SharedPointer::clone(&ptr); 93 | 94 | let ptr_clone = SharedPointer::try_unwrap(ptr_clone).unwrap_err(); 95 | let ptr = SharedPointer::try_unwrap(ptr).unwrap_err(); 96 | 97 | assert_eq!(*ptr, 42); 98 | assert_eq!(*ptr_clone, 42); 99 | } 100 | 101 | #[test] 102 | fn test_get_mut() { 103 | let mut ptr: SharedPointer<_, RcK> = SharedPointer::new(42); 104 | 105 | assert_eq!(*ptr, 42); 106 | 107 | *SharedPointer::get_mut(&mut ptr).unwrap() += 1; 108 | 109 | assert_eq!(*ptr, 43); 110 | 111 | let mut ptr_clone = SharedPointer::clone(&ptr); 112 | 113 | assert_eq!(SharedPointer::get_mut(&mut ptr), None); 114 | assert_eq!(SharedPointer::get_mut(&mut ptr_clone), None); 115 | 116 | mem::drop(ptr); 117 | 118 | *SharedPointer::get_mut(&mut ptr_clone).unwrap() += 1; 119 | 120 | assert_eq!(*ptr_clone, 44); 121 | } 122 | 123 | #[test] 124 | fn test_strong_count() { 125 | let ptr: SharedPointer<_, RcK> = SharedPointer::new(42); 126 | 127 | assert_eq!(SharedPointer::strong_count(&ptr), 1); 128 | 129 | let ptr_clone = SharedPointer::clone(&ptr); 130 | 131 | assert_eq!(SharedPointer::strong_count(&ptr), 2); 132 | assert_eq!(SharedPointer::strong_count(&ptr_clone), 2); 133 | 134 | mem::drop(ptr); 135 | 136 | assert_eq!(SharedPointer::strong_count(&ptr_clone), 1); 137 | } 138 | 139 | #[test] 140 | fn test_ptr_eq() { 141 | let ptr: SharedPointer<_, RcK> = SharedPointer::new(42); 142 | let ptr_same_content: SharedPointer<_, RcK> = SharedPointer::new(42); 143 | let ptr_clone: SharedPointer<_, _> = SharedPointer::clone(&ptr); 144 | 145 | assert!(SharedPointer::ptr_eq(&ptr, &ptr)); 146 | assert!(!SharedPointer::ptr_eq(&ptr, &ptr_same_content)); 147 | assert!(SharedPointer::ptr_eq(&ptr, &ptr_clone)); 148 | } 149 | 150 | #[test] 151 | fn test_make_mut() { 152 | let mut ptr: SharedPointer<_, RcK> = SharedPointer::new(42); 153 | 154 | assert_eq!(*ptr, 42); 155 | 156 | *SharedPointer::make_mut(&mut ptr) += 1; 157 | 158 | assert_eq!(*ptr, 43); 159 | 160 | // Clone to force make_mut to clone the data. 161 | let mut ptr_clone = SharedPointer::clone(&ptr); 162 | 163 | assert_eq!(*ptr_clone, 43); 164 | 165 | *SharedPointer::make_mut(&mut ptr_clone) += 1; 166 | 167 | assert_eq!(*ptr, 43); 168 | assert_eq!(*ptr_clone, 44); 169 | 170 | *SharedPointer::make_mut(&mut ptr) *= 2; 171 | 172 | assert_eq!(*ptr, 2 * 43); 173 | assert_eq!(*ptr_clone, 44); 174 | 175 | mem::drop(ptr); 176 | 177 | assert_eq!(*ptr_clone, 44); 178 | } 179 | 180 | #[test] 181 | fn test_clone() { 182 | let ptr: SharedPointer<_, RcK> = SharedPointer::new(Cell::new(42)); 183 | let ptr_clone = SharedPointer::clone(&ptr); 184 | 185 | assert_eq!(ptr.get(), 42); 186 | assert_eq!(ptr_clone.get(), 42); 187 | 188 | ptr_clone.set(3); 189 | 190 | assert_eq!(ptr.get(), 3); 191 | assert_eq!(ptr_clone.get(), 3); 192 | 193 | mem::drop(ptr); 194 | 195 | assert_eq!(ptr_clone.get(), 3); 196 | } 197 | 198 | fn hash(pointer: &SharedPointer) -> u64 { 199 | let mut hasher = std::collections::hash_map::DefaultHasher::new(); 200 | 201 | pointer.hash(&mut hasher); 202 | 203 | hasher.finish() 204 | } 205 | 206 | #[test] 207 | fn test_hash() { 208 | let ptr_42: SharedPointer<_, RcK> = SharedPointer::new(42); 209 | let ptr_hello: SharedPointer<_, RcK> = SharedPointer::new("hello"); 210 | 211 | assert_eq!(hash(&ptr_42), hash(&SharedPointer::<_, RcK>::new(42))); 212 | assert_eq!(hash(&ptr_hello), hash(&SharedPointer::<_, RcK>::new("hello"))); 213 | } 214 | 215 | #[test] 216 | fn test_hash_pointer_kind_consistent() { 217 | let ptr_hello_rc: SharedPointer<_, RcK> = SharedPointer::new("hello"); 218 | let ptr_hello_arc: SharedPointer<_, ArcK> = SharedPointer::new("hello"); 219 | 220 | assert_eq!(hash(&ptr_hello_rc), hash(&ptr_hello_arc)); 221 | } 222 | 223 | #[allow(clippy::nonminimal_bool)] 224 | #[test] 225 | fn test_eq() { 226 | let ptr_22: SharedPointer<_, RcK> = SharedPointer::new(22); 227 | let ptr_42: SharedPointer<_, RcK> = SharedPointer::new(42); 228 | 229 | assert!(ptr_22 == SharedPointer::<_, RcK>::new(22)); 230 | assert!(ptr_22 == SharedPointer::<_, ArcK>::new(22)); 231 | assert!(ptr_22 == ptr_22); 232 | assert!(!(ptr_22 == SharedPointer::<_, RcK>::new(42))); 233 | assert!(!(ptr_22 == SharedPointer::<_, ArcK>::new(42))); 234 | assert!(!(ptr_22 == ptr_42)); 235 | 236 | assert!(ptr_22 != SharedPointer::<_, RcK>::new(42)); 237 | assert!(ptr_22 != SharedPointer::<_, ArcK>::new(42)); 238 | assert!(ptr_22 != ptr_42); 239 | assert!(!(ptr_22 != SharedPointer::<_, RcK>::new(22))); 240 | assert!(!(ptr_22 != SharedPointer::<_, ArcK>::new(22))); 241 | assert!(!(ptr_22 != ptr_22)); 242 | } 243 | 244 | #[allow(clippy::cognitive_complexity)] 245 | #[allow(clippy::nonminimal_bool)] 246 | #[test] 247 | fn test_ord() { 248 | let ptr_22: SharedPointer<_, RcK> = SharedPointer::new(22); 249 | let ptr_42: SharedPointer<_, RcK> = SharedPointer::new(42); 250 | 251 | assert_eq!(ptr_22.partial_cmp(&SharedPointer::<_, RcK>::new(22)), Some(Ordering::Equal)); 252 | assert_eq!(ptr_22.partial_cmp(&SharedPointer::<_, RcK>::new(42)), Some(Ordering::Less)); 253 | assert_eq!(ptr_42.partial_cmp(&SharedPointer::<_, RcK>::new(22)), Some(Ordering::Greater)); 254 | 255 | assert_eq!(ptr_22.cmp(&SharedPointer::<_, RcK>::new(22)), Ordering::Equal); 256 | assert_eq!(ptr_22.cmp(&SharedPointer::<_, RcK>::new(42)), Ordering::Less); 257 | assert_eq!(ptr_42.cmp(&SharedPointer::<_, RcK>::new(22)), Ordering::Greater); 258 | 259 | assert!(ptr_22 < SharedPointer::<_, RcK>::new(42)); 260 | assert!(ptr_22 < SharedPointer::<_, ArcK>::new(42)); 261 | assert!(ptr_22 < ptr_42); 262 | assert!(!(ptr_42 < SharedPointer::<_, RcK>::new(22))); 263 | assert!(!(ptr_42 < SharedPointer::<_, ArcK>::new(22))); 264 | assert!(!(ptr_42 < ptr_22)); 265 | assert!(!(ptr_22 < ptr_22)); 266 | 267 | assert!(ptr_22 <= SharedPointer::<_, RcK>::new(42)); 268 | assert!(ptr_22 <= SharedPointer::<_, ArcK>::new(42)); 269 | assert!(ptr_22 <= ptr_42); 270 | assert!(ptr_22 <= ptr_22); 271 | assert!(!(ptr_42 <= SharedPointer::<_, RcK>::new(22))); 272 | assert!(!(ptr_42 <= SharedPointer::<_, ArcK>::new(22))); 273 | assert!(!(ptr_42 <= ptr_22)); 274 | 275 | assert!(ptr_42 > SharedPointer::<_, RcK>::new(22)); 276 | assert!(ptr_42 > SharedPointer::<_, ArcK>::new(22)); 277 | assert!(ptr_42 > ptr_22); 278 | assert!(!(ptr_22 > SharedPointer::<_, RcK>::new(42))); 279 | assert!(!(ptr_22 > SharedPointer::<_, ArcK>::new(42))); 280 | assert!(!(ptr_22 > ptr_42)); 281 | assert!(!(ptr_42 > ptr_42)); 282 | 283 | assert!(ptr_42 >= SharedPointer::<_, RcK>::new(22)); 284 | assert!(ptr_42 >= SharedPointer::<_, ArcK>::new(22)); 285 | assert!(ptr_42 >= ptr_22); 286 | assert!(ptr_42 >= ptr_42); 287 | assert!(!(ptr_22 >= SharedPointer::<_, RcK>::new(42))); 288 | assert!(!(ptr_22 >= SharedPointer::<_, ArcK>::new(42))); 289 | assert!(!(ptr_22 >= ptr_42)); 290 | } 291 | 292 | #[test] 293 | fn test_default() { 294 | let ptr: SharedPointer = SharedPointer::default(); 295 | 296 | assert_eq!(*ptr, 0); 297 | } 298 | 299 | #[test] 300 | fn test_from_box_t() { 301 | let ptr: SharedPointer = SharedPointer::from(Box::new(42)); 302 | 303 | assert_eq!(*ptr, 42); 304 | } 305 | 306 | #[test] 307 | fn test_from_t() { 308 | let ptr: SharedPointer = SharedPointer::from(42); 309 | 310 | assert_eq!(*ptr, 42); 311 | } 312 | 313 | #[test] 314 | fn test_debug() { 315 | let ptr: SharedPointer<_, RcK> = SharedPointer::new([1, 2, 3]); 316 | 317 | assert_eq!(format!("{ptr:?}"), "[1, 2, 3]"); 318 | } 319 | 320 | #[cfg(not(miri))] // Miri doesn't like this one. 321 | #[test] 322 | fn test_fmt_pointer() { 323 | let ptr: SharedPointer<_, RcK> = SharedPointer::new(314); 324 | 325 | assert_eq!(format!("{ptr:p}"), format!("{:p}", ptr::from_ref::(ptr.deref()))); 326 | } 327 | 328 | #[test] 329 | fn test_display() { 330 | let ptr: SharedPointer<_, RcK> = SharedPointer::new("hello"); 331 | 332 | assert_eq!(format!("{ptr}"), "hello"); 333 | } 334 | 335 | #[cfg(feature = "serde")] 336 | #[test] 337 | fn test_serde() { 338 | let ptr: SharedPointer<_, RcK> = SharedPointer::new("hello"); 339 | 340 | let encoded = serde_json::to_string(&ptr).unwrap(); 341 | let decoded: SharedPointer<_, RcK> = serde_json::from_str(&encoded).unwrap(); 342 | 343 | pretty_assertions::assert_eq!(ptr, decoded); 344 | } 345 | -------------------------------------------------------------------------------- /tools/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname "$0") 6 | cd "$(git rev-parse --show-toplevel)" 7 | 8 | source "tools/utils.sh" 9 | 10 | RED='\033[0;31m' 11 | GREEN='\033[0;32m' 12 | NC='\033[0m' 13 | 14 | function on_failure { 15 | echo >&2 16 | echo -e "${RED}Whoopsie-daisy: something failed!$NC" >&2 17 | } 18 | 19 | assert_installed "cargo" 20 | 21 | trap on_failure ERR 22 | 23 | function check_basic { 24 | echo 'Building:' 25 | cargo build --features fatal-warnings --all-targets 26 | echo 'Testing:' 27 | cargo test --features fatal-warnings --all-targets 28 | # Weirdly, the `cargo test ... --all-targets ...` above does not run the tests in the documentation, so we run the 29 | # doc tests like this. 30 | # See https://github.com/rust-lang/cargo/issues/6669. 31 | echo 'Testing doc:' 32 | cargo test --features fatal-warnings --doc 33 | echo 'Checking the benchmarks:' 34 | cargo bench --features fatal-warnings -- --test 35 | echo 'Checking documentation:' 36 | cargo doc --features fatal-warnings --no-deps 37 | 38 | # Tests for memory safety and memory leaks with miri. 39 | if [ -z "$MIRI_TOOLCHAIN" ]; then 40 | MIRI_TOOLCHAIN=nightly 41 | fi 42 | echo "Testing with miri (with toolchain $MIRI_TOOLCHAIN):" 43 | cargo +$MIRI_TOOLCHAIN miri test --all-features 44 | } 45 | 46 | function check_doc_url_links { 47 | assert_installed "cargo-deadlinks" 48 | 49 | echo 'Checking doc url links:' 50 | cargo deadlinks 51 | } 52 | 53 | function check_unused_deps { 54 | assert_installed "cargo-machete" 55 | 56 | echo 'Checking unused dependencies:' 57 | cargo machete 58 | } 59 | 60 | function check_packaging { 61 | echo 'Checking packaging:' 62 | cargo package --allow-dirty 63 | } 64 | 65 | function check_fmt { 66 | assert_installed "cargo-fmt" 67 | 68 | echo 'Checking code format:' 69 | cargo fmt -- --check 70 | } 71 | 72 | function check_toml_fmt { 73 | assert_installed "taplo" 74 | 75 | echo 'Checking toml format:' 76 | taplo fmt --check 77 | } 78 | 79 | function check_readme { 80 | assert_installed "cargo-rdme" 81 | 82 | echo 'Checking readme:' 83 | cargo rdme --check 84 | } 85 | 86 | function check_msrv { 87 | assert_installed "cargo-msrv" 88 | 89 | echo 'Checking the minimum supported rust version:' 90 | cargo msrv verify 91 | } 92 | 93 | function check_clippy { 94 | assert_installed "cargo-clippy" 95 | 96 | echo 'Checking with clippy:' 97 | cargo clippy --all-targets -- -D warnings 98 | } 99 | 100 | to_run=(basic doc_url_links unused_deps packaging fmt toml_fmt readme msrv clippy) 101 | 102 | if [ $# -ge 1 ]; then 103 | to_run=("$@") 104 | fi 105 | 106 | for check in "${to_run[@]}"; do 107 | check_$check 108 | done 109 | 110 | echo 111 | echo -e "${GREEN}Everything looks lovely!$NC" 112 | 113 | exit 0 114 | -------------------------------------------------------------------------------- /tools/codecov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname "$0") 6 | cd "$(git rev-parse --show-toplevel)" 7 | 8 | source "tools/utils.sh" 9 | 10 | assert_installed "cargo-tarpaulin" 11 | 12 | output_format=html 13 | 14 | args=$(getopt -o '' -l xml -- "$@") 15 | 16 | eval set -- "$args" 17 | 18 | while [ $# -ge 1 ]; do 19 | case "$1" in 20 | --) 21 | # No more options left. 22 | shift 23 | break 24 | ;; 25 | --xml) 26 | output_format=xml 27 | ;; 28 | esac 29 | 30 | shift 31 | done 32 | 33 | # TODO it seems the `--force-clean` is not working. 34 | cargo clean 35 | cargo tarpaulin --force-clean --ignore-panics --engine llvm --timeout 1200 --out $output_format \ 36 | --run-types Lib --all-features 37 | 38 | if [ "$output_format" == "html" ]; then 39 | echo 40 | echo "You can find the test coverage results at file://$(pwd)/tarpaulin-report.html" 41 | fi 42 | -------------------------------------------------------------------------------- /tools/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | MAIN_BRANCH=main 6 | 7 | cd $(dirname "$0") 8 | cd "$(git rev-parse --show-toplevel)" 9 | 10 | source "tools/utils.sh" 11 | 12 | function set_version { 13 | local version=$1 14 | 15 | sed -i "0,/version = .*$/s//version = \"$version\"/" Cargo.toml 16 | 17 | # Update version in `Cargo.lock`. 18 | cargo update -w --offline 19 | } 20 | 21 | if [ $(git symbolic-ref --short HEAD) != $MAIN_BRANCH ]; then 22 | echo "Not in $MAIN_BRANCH branch." >&2 23 | exit 1 24 | fi 25 | 26 | if [ $(git status --porcelain --untracked-files=no | wc -l) -ne 0 ]; then 27 | echo "Working directory is not clean." >&2 28 | exit 1 29 | fi 30 | 31 | echo 'Checking if local branch is up to date...' 32 | 33 | git remote update origin > /dev/null 2> /dev/null 34 | 35 | if git status -uno | grep --silent "behind"; then 36 | echo "Local branch is not up to date." >&2 37 | exit 1 38 | fi 39 | 40 | echo "Current version is $(project_version)." 41 | 42 | echo -n "Which version do you want to release? " 43 | read release_version 44 | 45 | echo -n "Which will be the next version? " 46 | read next_version 47 | 48 | if ! echo "$next_version" | grep --silent -- "-pre$"; then 49 | echo 'Next version must end in `-pre`.' >&2 50 | fi 51 | 52 | echo 53 | echo "Current version: $(project_version)" 54 | echo "Release version: $release_version" 55 | echo "Next version: $next_version" 56 | echo 57 | echo -n 'Does this look right [yes/no]? ' 58 | 59 | read answer 60 | 61 | if [ "$answer" != "yes" ]; then 62 | exit 0 63 | fi 64 | 65 | echo -n "Running tests... " 66 | 67 | if ! ./tools/check.sh 2>/dev/null > /dev/null; then 68 | echo "It failed :(" >&2 69 | exit 0 70 | fi 71 | 72 | echo "done." 73 | 74 | while ! grep "^## " release-notes.md | head -1 | grep --silent "^## $release_version$"; do 75 | echo 76 | echo "There's no entry for this version in the release notes." 77 | echo -n "Go ahead and add them and press enter when you're done... " 78 | read 79 | done 80 | 81 | set_version "$release_version" 82 | 83 | git commit -am "Release v${release_version}." 84 | git tag --sign -a "v${release_version}" -m "$(project_name) version ${release_version}." 85 | 86 | set_version "$next_version" 87 | 88 | git commit -am "Bump to version $next_version." 89 | 90 | echo "Check if everything is alright. If so do:" 91 | echo 92 | echo " git push --atomic origin $MAIN_BRANCH v${release_version} && git checkout v${release_version} && cargo publish && git checkout $MAIN_BRANCH" 93 | echo 94 | -------------------------------------------------------------------------------- /tools/utils.sh: -------------------------------------------------------------------------------- 1 | function assert_installed { 2 | local bin="$1" 3 | 4 | if ! [ -x "$(which "$bin" 2> /dev/null)" ]; then 5 | echo "error: $bin not installed." >&2 6 | exit 1 7 | fi 8 | } 9 | 10 | function project_name { 11 | cargo pkgid | tac -s'/' | head -1 | cut -d'#' -f1 12 | } 13 | 14 | function project_version { 15 | cargo pkgid | tac -s'/' | head -1 | cut -d'#' -f2 16 | } 17 | --------------------------------------------------------------------------------