pub struct Accessor<T, F> { /* private fields */ }Expand description
A small, copyable accessor that focuses into a field F inside a root T.
Representation: a byte offset from the start of T to the field F. This allows cheap composition by offset addition. All operations are implemented via unsafe pointer arithmetic but expose a safe API.
Implementations§
Source§impl<T, F> Accessor<T, F>
impl<T, F> Accessor<T, F>
Sourcepub const unsafe fn from_offset(offset: isize) -> Accessor<T, F>
pub const unsafe fn from_offset(offset: isize) -> Accessor<T, F>
Construct from a precomputed byte offset.
§Safety
The offset must satisfy all of the following conditions for every valid instance of T:
- It is the exact byte distance from the start of
Tto the target fieldFwithin the same allocation (i.e., derived from an actual field projection ofT). - The resulting pointer computed as
(&T as *const u8).offset(offset) as *const Fis properly aligned forFand points to initialized memory owned by the sameTobject. - The accessor will only ever be used with values of type
Tthat have the same layout with respect to the fieldF(e.g., not a different type or transmuted layout).
Violating any of these preconditions is undefined behavior. Prefer constructing accessors
via #[derive(Accessor)] or Accessor::from_fns, which compute valid offsets for you.
Sourcepub fn from_fns(
get_ref: fn(&T) -> &F,
_get_mut: fn(&mut T) -> &mut F,
) -> Accessor<T, F>
pub fn from_fns( get_ref: fn(&T) -> &F, _get_mut: fn(&mut T) -> &mut F, ) -> Accessor<T, F>
Runtime constructor from field-selection functions. Computes the offset using raw pointer projection without dereferencing invalid memory.
Sourcepub fn get<'a>(&self, root: &'a T) -> &'a F
pub fn get<'a>(&self, root: &'a T) -> &'a F
Borrow the focused field immutably.
Examples found in repository?
37fn main() {
38 let mut user = User {
39 profile: Profile {
40 address: Address {
41 city: "berlin".into(),
42 zip: 10115,
43 },
44 stats: Stats { logins: 1 },
45 },
46 settings: Settings {
47 theme: Theme {
48 name: "light".into(),
49 },
50 },
51 };
52
53 // Compose a deep accessor: User -> Profile -> Address -> city
54 let acc_city = User::acc_profile()
55 .compose(Profile::acc_address())
56 .compose(Address::acc_city());
57
58 println!("city before: {}", acc_city.get(&user));
59
60 // In-place deep mutation via set_mut
61 acc_city.set_mut(&mut user, |c| c.make_ascii_uppercase());
62 println!("city upper: {}", acc_city.get(&user));
63
64 // Set via cloning only the leaf value
65 let new_city = String::from("Lund");
66 acc_city.set_clone(&mut user, &new_city);
67 println!("city after set_clone: {}", acc_city.get(&user));
68
69 // Compose another deep accessor: User -> Settings -> Theme -> name
70 let acc_theme_name = User::acc_settings()
71 .compose(Settings::acc_theme())
72 .compose(Theme::acc_name());
73
74 acc_theme_name.set(&mut user, "dark".to_string());
75 println!("theme after set: {}", acc_theme_name.get(&user));
76
77 // Show a non-string leaf with arithmetic updates
78 let acc_zip = User::acc_profile()
79 .compose(Profile::acc_address())
80 .compose(Address::acc_zip());
81
82 acc_zip.set_mut(&mut user, |z| *z += 5);
83 println!("zip after +5: {}", acc_zip.get(&user));
84}Sourcepub fn set(&self, root: &mut T, value: F)
pub fn set(&self, root: &mut T, value: F)
Set by moving a new value into the focused location.
Examples found in repository?
37fn main() {
38 let mut user = User {
39 profile: Profile {
40 address: Address {
41 city: "berlin".into(),
42 zip: 10115,
43 },
44 stats: Stats { logins: 1 },
45 },
46 settings: Settings {
47 theme: Theme {
48 name: "light".into(),
49 },
50 },
51 };
52
53 // Compose a deep accessor: User -> Profile -> Address -> city
54 let acc_city = User::acc_profile()
55 .compose(Profile::acc_address())
56 .compose(Address::acc_city());
57
58 println!("city before: {}", acc_city.get(&user));
59
60 // In-place deep mutation via set_mut
61 acc_city.set_mut(&mut user, |c| c.make_ascii_uppercase());
62 println!("city upper: {}", acc_city.get(&user));
63
64 // Set via cloning only the leaf value
65 let new_city = String::from("Lund");
66 acc_city.set_clone(&mut user, &new_city);
67 println!("city after set_clone: {}", acc_city.get(&user));
68
69 // Compose another deep accessor: User -> Settings -> Theme -> name
70 let acc_theme_name = User::acc_settings()
71 .compose(Settings::acc_theme())
72 .compose(Theme::acc_name());
73
74 acc_theme_name.set(&mut user, "dark".to_string());
75 println!("theme after set: {}", acc_theme_name.get(&user));
76
77 // Show a non-string leaf with arithmetic updates
78 let acc_zip = User::acc_profile()
79 .compose(Profile::acc_address())
80 .compose(Address::acc_zip());
81
82 acc_zip.set_mut(&mut user, |z| *z += 5);
83 println!("zip after +5: {}", acc_zip.get(&user));
84}Sourcepub fn set_mut(&self, root: &mut T, f: impl FnOnce(&mut F))
pub fn set_mut(&self, root: &mut T, f: impl FnOnce(&mut F))
Mutate the focused location in-place using the provided closure.
Examples found in repository?
37fn main() {
38 let mut user = User {
39 profile: Profile {
40 address: Address {
41 city: "berlin".into(),
42 zip: 10115,
43 },
44 stats: Stats { logins: 1 },
45 },
46 settings: Settings {
47 theme: Theme {
48 name: "light".into(),
49 },
50 },
51 };
52
53 // Compose a deep accessor: User -> Profile -> Address -> city
54 let acc_city = User::acc_profile()
55 .compose(Profile::acc_address())
56 .compose(Address::acc_city());
57
58 println!("city before: {}", acc_city.get(&user));
59
60 // In-place deep mutation via set_mut
61 acc_city.set_mut(&mut user, |c| c.make_ascii_uppercase());
62 println!("city upper: {}", acc_city.get(&user));
63
64 // Set via cloning only the leaf value
65 let new_city = String::from("Lund");
66 acc_city.set_clone(&mut user, &new_city);
67 println!("city after set_clone: {}", acc_city.get(&user));
68
69 // Compose another deep accessor: User -> Settings -> Theme -> name
70 let acc_theme_name = User::acc_settings()
71 .compose(Settings::acc_theme())
72 .compose(Theme::acc_name());
73
74 acc_theme_name.set(&mut user, "dark".to_string());
75 println!("theme after set: {}", acc_theme_name.get(&user));
76
77 // Show a non-string leaf with arithmetic updates
78 let acc_zip = User::acc_profile()
79 .compose(Profile::acc_address())
80 .compose(Address::acc_zip());
81
82 acc_zip.set_mut(&mut user, |z| *z += 5);
83 println!("zip after +5: {}", acc_zip.get(&user));
84}Sourcepub fn set_clone(&self, root: &mut T, value: &F)where
F: Clone,
pub fn set_clone(&self, root: &mut T, value: &F)where
F: Clone,
Set by cloning the provided value into the focused location.
MVP semantics:
- The caller provides a shared reference to the value, and we perform a top-level
Cloneof that value, then move it into the field. - Only
F: Cloneis required; the root typeTdoes not need to implementClone. - This behavior composes: for a composed accessor focusing
T -> ... -> V, callingset_cloneonly requiresV: Clone.
Examples found in repository?
37fn main() {
38 let mut user = User {
39 profile: Profile {
40 address: Address {
41 city: "berlin".into(),
42 zip: 10115,
43 },
44 stats: Stats { logins: 1 },
45 },
46 settings: Settings {
47 theme: Theme {
48 name: "light".into(),
49 },
50 },
51 };
52
53 // Compose a deep accessor: User -> Profile -> Address -> city
54 let acc_city = User::acc_profile()
55 .compose(Profile::acc_address())
56 .compose(Address::acc_city());
57
58 println!("city before: {}", acc_city.get(&user));
59
60 // In-place deep mutation via set_mut
61 acc_city.set_mut(&mut user, |c| c.make_ascii_uppercase());
62 println!("city upper: {}", acc_city.get(&user));
63
64 // Set via cloning only the leaf value
65 let new_city = String::from("Lund");
66 acc_city.set_clone(&mut user, &new_city);
67 println!("city after set_clone: {}", acc_city.get(&user));
68
69 // Compose another deep accessor: User -> Settings -> Theme -> name
70 let acc_theme_name = User::acc_settings()
71 .compose(Settings::acc_theme())
72 .compose(Theme::acc_name());
73
74 acc_theme_name.set(&mut user, "dark".to_string());
75 println!("theme after set: {}", acc_theme_name.get(&user));
76
77 // Show a non-string leaf with arithmetic updates
78 let acc_zip = User::acc_profile()
79 .compose(Profile::acc_address())
80 .compose(Address::acc_zip());
81
82 acc_zip.set_mut(&mut user, |z| *z += 5);
83 println!("zip after +5: {}", acc_zip.get(&user));
84}Sourcepub fn compose<V>(self, next: Accessor<F, V>) -> Accessor<T, V>
pub fn compose<V>(self, next: Accessor<F, V>) -> Accessor<T, V>
Compose this accessor with another, yielding an accessor from T to V.
Given self: Accessor<T, U> and next: Accessor<U, V>, returns
Accessor<T, V> that focuses by first going through self then next.
Examples found in repository?
37fn main() {
38 let mut user = User {
39 profile: Profile {
40 address: Address {
41 city: "berlin".into(),
42 zip: 10115,
43 },
44 stats: Stats { logins: 1 },
45 },
46 settings: Settings {
47 theme: Theme {
48 name: "light".into(),
49 },
50 },
51 };
52
53 // Compose a deep accessor: User -> Profile -> Address -> city
54 let acc_city = User::acc_profile()
55 .compose(Profile::acc_address())
56 .compose(Address::acc_city());
57
58 println!("city before: {}", acc_city.get(&user));
59
60 // In-place deep mutation via set_mut
61 acc_city.set_mut(&mut user, |c| c.make_ascii_uppercase());
62 println!("city upper: {}", acc_city.get(&user));
63
64 // Set via cloning only the leaf value
65 let new_city = String::from("Lund");
66 acc_city.set_clone(&mut user, &new_city);
67 println!("city after set_clone: {}", acc_city.get(&user));
68
69 // Compose another deep accessor: User -> Settings -> Theme -> name
70 let acc_theme_name = User::acc_settings()
71 .compose(Settings::acc_theme())
72 .compose(Theme::acc_name());
73
74 acc_theme_name.set(&mut user, "dark".to_string());
75 println!("theme after set: {}", acc_theme_name.get(&user));
76
77 // Show a non-string leaf with arithmetic updates
78 let acc_zip = User::acc_profile()
79 .compose(Profile::acc_address())
80 .compose(Address::acc_zip());
81
82 acc_zip.set_mut(&mut user, |z| *z += 5);
83 println!("zip after +5: {}", acc_zip.get(&user));
84}