From 3dbf10828b9b4b480190fa0090fd0b41e5186beb Mon Sep 17 00:00:00 2001 From: Chivalrik <77893938+fitzchivalrik@users.noreply.github.com> Date: Sat, 8 Apr 2023 16:04:14 +0200 Subject: [PATCH 1/5] feat: mention struct update syntax When talking about the `with` expression in C#, mention the struct update syntax [1] as something similar in Rust. [1]: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax --- src/language/custom-types/members.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/language/custom-types/members.md b/src/language/custom-types/members.md index 0616770..58d9b5e 100644 --- a/src/language/custom-types/members.md +++ b/src/language/custom-types/members.md @@ -309,10 +309,11 @@ Console.WriteLine(pt.ToString()); // prints: Point { X = 789, Y = 456 } readonly record struct Point(int X, int Y); ``` -There is no `with` in Rust, but to emulate something similar in Rust, it has -to be baked into the type's design: +There is no `with` in Rust, but the struct update syntax can be used +similar. This moves the original value, however: ```rust +#[derive(Debug)] struct Point { x: i32, y: i32 } impl Point { @@ -322,10 +323,11 @@ impl Point { pub fn x(&self) -> i32 { self.x } pub fn y(&self) -> i32 { self.y } +} - // following methods consume self and return a new instance - - pub fn set_x(self, val: i32) -> Self { Self::new(val, self.y) } - pub fn set_y(self, val: i32) -> Self { Self::new(self.x, val) } +fn main() { + let pt = Point::new(123, 456); + let pt = Point { x: 789, ..pt }; + println!("{pt:?}"); // prints: Point { x: 789, y: 456 } } ``` From 48c1e3766bb481c0562de602d2551129dde3acac Mon Sep 17 00:00:00 2001 From: Chivalrik <77893938+fitzchivalrik@users.noreply.github.com> Date: Sat, 8 Apr 2023 17:08:13 +0200 Subject: [PATCH 2/5] feat: mention struct update syntax, but as additional info Add struct update syntax as additional information, do not remove mention of explicit constructs to emulate a `with`. Also mention that fields need to be accessible to use the syntax. --- src/language/custom-types/members.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/language/custom-types/members.md b/src/language/custom-types/members.md index 58d9b5e..ce15181 100644 --- a/src/language/custom-types/members.md +++ b/src/language/custom-types/members.md @@ -309,11 +309,10 @@ Console.WriteLine(pt.ToString()); // prints: Point { X = 789, Y = 456 } readonly record struct Point(int X, int Y); ``` -There is no `with` in Rust, but the struct update syntax can be used -similar. This moves the original value, however: +There is no `with` in Rust, but to emulate something similar in Rust, it has +to be baked into the type's design: ```rust -#[derive(Debug)] struct Point { x: i32, y: i32 } impl Point { @@ -323,11 +322,28 @@ impl Point { pub fn x(&self) -> i32 { self.x } pub fn y(&self) -> i32 { self.y } + + // following methods consume self and return a new instance + + pub fn set_x(self, val: i32) -> Self { Self::new(val, self.y) } + pub fn set_y(self, val: i32) -> Self { Self::new(self.x, val) } +} +``` + +It is also possible to use the struct update syntax to achieve something similar. This moves the original value: + +```rust +mod points { + #[derive(Debug)] + pub struct Point { pub x: i32, pub y: i32 } } fn main() { - let pt = Point::new(123, 456); + use points::Point; + let pt = Point { x: 123, y: 456 }; let pt = Point { x: 789, ..pt }; println!("{pt:?}"); // prints: Point { x: 789, y: 456 } -} -``` +}``` + +As seen in the example above, the struct update syntax requires access to the type fields. +Therefore, it is generally more common to use it within the module that has access to private details of its types. From 8ea05c58d363d9f335f35bfb133cb54850583d5c Mon Sep 17 00:00:00 2001 From: Chivalrik <77893938+fitzchivalrik@users.noreply.github.com> Date: Sat, 8 Apr 2023 17:09:09 +0200 Subject: [PATCH 3/5] fix: markdown code block syntax --- src/language/custom-types/members.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/language/custom-types/members.md b/src/language/custom-types/members.md index ce15181..d226462 100644 --- a/src/language/custom-types/members.md +++ b/src/language/custom-types/members.md @@ -343,7 +343,8 @@ fn main() { let pt = Point { x: 123, y: 456 }; let pt = Point { x: 789, ..pt }; println!("{pt:?}"); // prints: Point { x: 789, y: 456 } -}``` +} +``` As seen in the example above, the struct update syntax requires access to the type fields. Therefore, it is generally more common to use it within the module that has access to private details of its types. From 3caa9f42a3c8be27c1c4d1009e1754c7d599f284 Mon Sep 17 00:00:00 2001 From: Chivalrik <77893938+fitzchivalrik@users.noreply.github.com> Date: Mon, 10 Apr 2023 10:10:36 +0200 Subject: [PATCH 4/5] feat: compare `with` on structs with update syntax Instead of record structs, as a plain struct is more comparable to Rust's. --- src/language/custom-types/members.md | 60 +++++++++++++++++----------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/language/custom-types/members.md b/src/language/custom-types/members.md index d226462..f4f1d56 100644 --- a/src/language/custom-types/members.md +++ b/src/language/custom-types/members.md @@ -302,15 +302,46 @@ impl Point { In C#, you can do non-destructive mutations using `with`: ```c# -var pt = new Point(123, 456); +struct Point +{ + public int X; + public int Y; + + public override string ToString() => $"({X}, {Y})"; +} + +var pt = new Point { X = 123, Y = 456 }; +Console.WriteLine(pt.ToString()); // prints: (123, 456) pt = pt with { X = 789 }; -Console.WriteLine(pt.ToString()); // prints: Point { X = 789, Y = 456 } +Console.WriteLine(pt.ToString()); // prints: (789, 456) +``` -readonly record struct Point(int X, int Y); +Rust has a _[struct update syntax]_ that may seem similar to `with`: + +```rust +mod points { + #[derive(Debug)] + pub struct Point { pub x: i32, pub y: i32 } +} + +fn main() { + use points::Point; + let pt = Point { x: 123, y: 456 }; + println!("{pt:?}"); // prints: Point { x: 123, y: 456 } + let pt = Point { x: 789, ..pt }; + println!("{pt:?}"); // prints: Point { x: 789, y: 456 } +} ``` -There is no `with` in Rust, but to emulate something similar in Rust, it has -to be baked into the type's design: +While `with` in C# does a non-destructive mutation (copy then update), +the [struct update syntax] does (partial) _moves_ and works fields only. +As seen in the example above, the syntax therefore requires access to +the type's fields. +Hence, it is generally more common to use it within the module that +has access to private details of its types. + +To emulate something similar to `with` in Rust, it has to be baked into +the type's design: ```rust struct Point { x: i32, y: i32 } @@ -330,21 +361,4 @@ impl Point { } ``` -It is also possible to use the struct update syntax to achieve something similar. This moves the original value: - -```rust -mod points { - #[derive(Debug)] - pub struct Point { pub x: i32, pub y: i32 } -} - -fn main() { - use points::Point; - let pt = Point { x: 123, y: 456 }; - let pt = Point { x: 789, ..pt }; - println!("{pt:?}"); // prints: Point { x: 789, y: 456 } -} -``` - -As seen in the example above, the struct update syntax requires access to the type fields. -Therefore, it is generally more common to use it within the module that has access to private details of its types. +[struct update syntax]: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax From d655d1bbc2a9149d7561ced78fc3847a0b8b562b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 13 Feb 2024 19:24:32 +0100 Subject: [PATCH 5/5] Review final edits --- src/language/custom-types/members.md | 68 ++++++++++++++++------------ 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/language/custom-types/members.md b/src/language/custom-types/members.md index 70e3131..4c488ab 100644 --- a/src/language/custom-types/members.md +++ b/src/language/custom-types/members.md @@ -301,6 +301,38 @@ impl Point { In C#, you can do non-destructive mutations using `with`: +```c# +var pt = new Point(123, 456); +pt = pt with { X = 789 }; +Console.WriteLine(pt.ToString()); // prints: Point { X = 789, Y = 456 } + +readonly record struct Point(int X, int Y); +``` + +There is no `with` in Rust, but to emulate something similar in Rust, it has +to be baked into the type's design: + +```rust +struct Point { x: i32, y: i32 } + +impl Point { + pub fn new(x: i32, y: i32) -> Self { + Self { x, y } + } + + pub fn x(&self) -> i32 { self.x } + pub fn y(&self) -> i32 { self.y } + + // following methods consume self and return a new instance + + pub fn set_x(self, val: i32) -> Self { Self::new(val, self.y) } + pub fn set_y(self, val: i32) -> Self { Self::new(self.x, val) } +} +``` + +In C#, `with` can also be used with a regular (as opposed to record) `struct` +that publicly exposes its read-write fields: + ```c# struct Point { @@ -316,7 +348,7 @@ pt = pt with { X = 789 }; Console.WriteLine(pt.ToString()); // prints: (789, 456) ``` -Rust has a _[struct update syntax]_ that may seem similar to `with`: +Rust has a _[struct update syntax]_ that may seem similar: ```rust mod points { @@ -333,32 +365,10 @@ fn main() { } ``` -While `with` in C# does a non-destructive mutation (copy then update), -the [struct update syntax] does (partial) _moves_ and works fields only. -As seen in the example above, the syntax therefore requires access to -the type's fields. -Hence, it is generally more common to use it within the module that -has access to private details of its types. - -To emulate something similar to `with` in Rust, it has to be baked into -the type's design: - -```rust -struct Point { x: i32, y: i32 } - -impl Point { - pub fn new(x: i32, y: i32) -> Self { - Self { x, y } - } - - pub fn x(&self) -> i32 { self.x } - pub fn y(&self) -> i32 { self.y } - - // following methods consume self and return a new instance - - pub fn set_x(self, val: i32) -> Self { Self::new(val, self.y) } - pub fn set_y(self, val: i32) -> Self { Self::new(self.x, val) } -} -``` +However, while `with` in C# does a non-destructive mutation (copy then +update), the [struct update syntax] does (partial) _moves_ and works with +fields only. Since the syntax requires access to the type's fields, it is +generally more common to use it within the Rust module that has access to +private details of its types. -[struct update syntax]: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax + [struct update syntax]: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax