cell_project/
lib.rs

1#![no_std]
2
3//! A safe interface to project through shared references to [`core::cell::Cell`](https://!doc.rust-lang.org/core/cell/struct.Cell.html).
4//!
5//! ```rust
6//! use std::cell::Cell;
7//! use cell_project::cell_project as cp; // renamed for ergonomics
8//!
9//! struct Point {
10//!     x: f32,
11//!     y: f32,
12//! }
13//!
14//! fn get_x_cell(point: &Cell<Point>) -> &Cell<f32> {
15//!     cp!(Point, point.x)
16//! }
17//! ```
18//!
19//! The syntax for the macro is as follows
20//!
21//! ```rust compile_fail
22//! let projection = cp!($TypeOfValue, $value_identifier.$field_identifier);
23//! ```
24//!
25//! You may not pass an expression for `$value_identifier`, if you need to then you should do.
26//!
27//! ```rust
28//! # cell_project::docs_example!{point}
29//! # fn get_point() -> Point { Point { x: 0.0, y: 0.0 } }
30//! let value = Cell::new(get_point());
31//! let projection = cp!(Point, value.y);
32//! ```
33//!
34//! If you need to project through multiple fields then you need to call `cp!` multiple times, once per projection
35//!
36//! ```rust
37//! # cell_project::docs_example!{point}
38//! struct Pair<T>(T, T);
39//!
40//! # fn some_point(some_pair: &Cell<Pair<Point>>) {
41//! // let some_pair: &Cell<Pair<Point>>;
42//! let point = cp!(Pair<Point>, some_pair.0);
43//! let x = cp!(Point, point.x);
44//! # }
45//! ```
46//!
47//! note: for generic types, you can use `_` to infer the generic parameters
48//!
49//! ```rust
50//! # cell_project::docs_example!{pair}
51//! fn get_x_cell<T>(point: &Cell<Pair<T>>) -> &Cell<T> {
52//!     cp!(Pair<_>, point.0)
53//! }
54//! ```
55//!
56//! Some limitations, you cannot project an enum variant because that is potentially unsound.
57//!
58//! ```rust compile_fail
59//! # cell_project::docs_example!{}
60//! let x = Cell::new(Some(0));
61//!
62//! // let's imagine a macro like `try_cell_project`, which takes a varaint as well as a type
63//! let proj = cell_project::try_cell_project!(Option<_>, Some, x.0).unwrap();
64//!
65//! x.set(None); // we can still write to the `Cell` directly
66//!
67//! // this will read uninitialized memory (because that's what `None` wrote in)
68//! // and there is no way to fix this. Enums cannot allow safe projection through
69//! // a shared mutable reference (like `&Cell<_>`)
70//! let _ = proj.get();
71//! ```
72//! so you cannot project through enums
73//!
74//! Another limitation of stable, you can only project to `Sized` types. For example, if I have a type
75//!
76//! ```rust
77//! struct Unsized(i32, [u8]);
78//! ```
79//! Then I can only project to the first field, because the second field is `!Sized`
80//!
81//! ## features
82//!
83//! `nightly` - unlocks [`cell_project::nightly_cell_project`](nightly_cell_project), which uses the unstable `#![feature(raw_ref_op)]` to
84//! allow projections to `!Sized` fields.
85
86#[doc(hidden)]
87pub mod macros;
88
89#[doc(hidden)]
90#[macro_export]
91macro_rules! docs_example {
92    () => {
93        use cell_project::cell_project as cp; // renamed for ergonomics
94        use std::cell::Cell;
95    };
96    (point) => {
97        use cell_project::cell_project as cp;
98        use std::cell::Cell; // renamed for ergonomics
99
100        struct Point {
101            x: f32,
102            y: f32,
103        }
104    };
105    (pair) => {
106        $crate::docs_example! {point}
107        pub struct Pair<T>(T, T);
108    };
109}
110
111/// project through a shared mutable reference `&Cell`
112///
113/// see the crate docs for more information
114#[macro_export]
115macro_rules! cell_project {
116    ($type:path, $ident:ident.$field:tt) => {
117        match $ident {
118            ref cell => unsafe {
119                let cell: &$crate::macros::Cell<$type> = cell;
120                let ptr = cell.as_ptr();
121                let $type { $field: _, .. } = &mut *ptr;
122                let field_ptr = $crate::macros::ptr::addr_of_mut!((*ptr).$field);
123                $crate::macros::project_unchecked(cell, field_ptr)
124            },
125        }
126    };
127}