From 6ca00236db60a64ea2ad402df0ae6c5464a2977a Mon Sep 17 00:00:00 2001 From: Devin Gunay Date: Mon, 17 Aug 2020 02:27:59 -0700 Subject: [PATCH 1/9] Create typestate.md Neat pattern for Rust that lets you encode valid/invalid states using the type system. --- patterns/typestate.md | 97 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 patterns/typestate.md diff --git a/patterns/typestate.md b/patterns/typestate.md new file mode 100644 index 00000000..11a207fd --- /dev/null +++ b/patterns/typestate.md @@ -0,0 +1,97 @@ +# The Typestate Pattern + +## Description + +(from http://cliffle.com/blog/rust-typestate/): + +The typestate pattern is an API design pattern that encodes information about an object's run-time state in its compile-time type. In particular, an API using the typestate pattern will have: + +Operations on an object (such as methods or functions) that are only available when the object is in certain states, + +A way of encoding these states at the type level, such that attempts to use the operations in the wrong state fail to compile, + +State transition operations (methods or functions) that change the type-level state of objects in addition to, or instead of, changing run-time dynamic state, such that the operations in the previous state are no longer possible. + +## Example + +This is one example of how to implement the pattern for a simple type that needs to be initialized or otherwise prepared before use. + +See http://cliffle.com/blog/rust-typestate/ for more in-depth examples and techniques. + +```rust + +use std::marker::PhantomData; + +pub struct Ready; + +pub struct Thing { + // tracks state type info at compile time, optimized out for runtime. + marker: PhantomData +} + +// Private constructor to internally control what state the struct is in. +fn state_constructor() -> Thing { + Thing { marker: PhantomData } +} + +// Operations in our default state () +impl Thing { + pub fn new() -> Self { + Self { marker: PhantomData } + } + + // Consumes the struct to return one with a new type state + pub fn get_ready(self) -> Thing { + state_constructor::() + } +} + +// Operations available in any state +impl Thing { + pub fn do_any_time(&self) { + println!("We can do this function whenever"); + } +} + +// We can only use this function when ready +pub fn do_only_when_ready(_: Thing) { + println!("We can only do this when we are Ready") +} + +fn main() { + let thing = Thing::new(); + + // Not ready yet + thing.do_any_time(); + // do_only_when_ready(thing); // this won't compile + + // Transition to Ready + let ready = thing.get_ready(); + + // Now we're ready + ready.do_any_time(); + do_only_when_ready(ready); +} +``` + + +## Motivation + +When creating a system that functions as a state machine, typestates allow you to prevent invalid states from ever occuring in any runtime scenario. + +## Advantages + +(again, from http://cliffle.com/blog/rust-typestate/) + +* It moves certain types of errors from run-time to compile-time, giving programmers faster feedback. +* It interacts nicely with IDEs, which can avoid suggesting operations that are illegal in a certain state. +* It can eliminate run-time checks, making code faster/smaller. + +## Disadvantages + +* It can add some verbosity and complexity to the code. +* Implementing it for complex structs can be difficult. + +## See also + +http://cliffle.com/blog/rust-typestate/ From e2477de96717897468913542ea128179a58b449e Mon Sep 17 00:00:00 2001 From: Devin Gunay Date: Mon, 17 Aug 2020 10:58:30 -0700 Subject: [PATCH 2/9] Update typestate.md Formatting and minor clarifications. --- patterns/typestate.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/patterns/typestate.md b/patterns/typestate.md index 11a207fd..57c18e60 100644 --- a/patterns/typestate.md +++ b/patterns/typestate.md @@ -6,15 +6,16 @@ The typestate pattern is an API design pattern that encodes information about an object's run-time state in its compile-time type. In particular, an API using the typestate pattern will have: -Operations on an object (such as methods or functions) that are only available when the object is in certain states, +* Operations on an object (such as methods or functions) that are only available when the object is in certain states, -A way of encoding these states at the type level, such that attempts to use the operations in the wrong state fail to compile, +* A way of encoding these states at the type level, such that attempts to use the operations in the wrong state fail to compile, -State transition operations (methods or functions) that change the type-level state of objects in addition to, or instead of, changing run-time dynamic state, such that the operations in the previous state are no longer possible. +* State transition operations (methods or functions) that change the type-level state of objects in addition to, or instead of, changing run-time dynamic state, such that the operations in the previous state are no longer possible. ## Example -This is one example of how to implement the pattern for a simple type that needs to be initialized or otherwise prepared before use. +This is one example of how to implement the pattern for a simple type that needs to be initialized or otherwise prepared before use +(that is, it has two states: (), and Ready). More states and operations may be added to implement a more complex state machine. See http://cliffle.com/blog/rust-typestate/ for more in-depth examples and techniques. @@ -77,7 +78,7 @@ fn main() { ## Motivation -When creating a system that functions as a state machine, typestates allow you to prevent invalid states from ever occuring in any runtime scenario. +You are modelling a system that functions as a state machine, and want to ensure at compile-time that invalid states never occur in any runtime scenario. ## Advantages From 497b474d678eabf3e3a0f72075cc9bb3578fa952 Mon Sep 17 00:00:00 2001 From: Devin Gunay Date: Sun, 3 Jan 2021 19:37:49 -0800 Subject: [PATCH 3/9] add link to typestate in SUMMARY.md --- SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SUMMARY.md b/SUMMARY.md index 850b495b..165e6cda 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -26,6 +26,7 @@ - [Prefer Small Crates](./patterns/small-crates.md) - [Contain unsafety in small modules](./patterns/unsafe-mods.md) - [Visitor](./patterns/visitor.md) + - [Typestate/Session Types](./patterns/typestate.md) - [Anti-patterns](./anti_patterns/README.md) - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md) From 45dc61661517c5d4935cc30b9615560af501a1dc Mon Sep 17 00:00:00 2001 From: Devin Gunay Date: Sun, 3 Jan 2021 19:38:51 -0800 Subject: [PATCH 4/9] Add another disadvantage of the typestate pattern --- patterns/typestate.md | 1 + 1 file changed, 1 insertion(+) diff --git a/patterns/typestate.md b/patterns/typestate.md index 57c18e60..baed8361 100644 --- a/patterns/typestate.md +++ b/patterns/typestate.md @@ -92,6 +92,7 @@ You are modelling a system that functions as a state machine, and want to ensure * It can add some verbosity and complexity to the code. * Implementing it for complex structs can be difficult. +* It can make compiler error messages very hard to understand. ## See also From df66673394fab05c60daf5c417145b20fbb9b9fd Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 4 Jan 2021 09:56:59 +0100 Subject: [PATCH 5/9] Update patterns/typestate.md --- patterns/typestate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/typestate.md b/patterns/typestate.md index baed8361..3987dda0 100644 --- a/patterns/typestate.md +++ b/patterns/typestate.md @@ -96,4 +96,4 @@ You are modelling a system that functions as a state machine, and want to ensure ## See also -http://cliffle.com/blog/rust-typestate/ + - [The Typestate Pattern in Rust, Cliff L. Biffle (2019)](https://web.archive.org/web/20210103081241/https://cliffle.com/blog/rust-typestate/) From f3df9ff0ea9b9207ce2c18a2aa59fe1320e3ada6 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 4 Jan 2021 09:57:12 +0100 Subject: [PATCH 6/9] Update patterns/typestate.md --- patterns/typestate.md | 1 - 1 file changed, 1 deletion(-) diff --git a/patterns/typestate.md b/patterns/typestate.md index 3987dda0..39f5a54a 100644 --- a/patterns/typestate.md +++ b/patterns/typestate.md @@ -82,7 +82,6 @@ You are modelling a system that functions as a state machine, and want to ensure ## Advantages -(again, from http://cliffle.com/blog/rust-typestate/) * It moves certain types of errors from run-time to compile-time, giving programmers faster feedback. * It interacts nicely with IDEs, which can avoid suggesting operations that are illegal in a certain state. From 639b0dd26694d5003cb8f306f9d629fc090ff95a Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 4 Jan 2021 09:57:21 +0100 Subject: [PATCH 7/9] Update patterns/typestate.md --- patterns/typestate.md | 1 - 1 file changed, 1 deletion(-) diff --git a/patterns/typestate.md b/patterns/typestate.md index 39f5a54a..5179f62a 100644 --- a/patterns/typestate.md +++ b/patterns/typestate.md @@ -17,7 +17,6 @@ The typestate pattern is an API design pattern that encodes information about an This is one example of how to implement the pattern for a simple type that needs to be initialized or otherwise prepared before use (that is, it has two states: (), and Ready). More states and operations may be added to implement a more complex state machine. -See http://cliffle.com/blog/rust-typestate/ for more in-depth examples and techniques. ```rust From 33be6e87d5435886fc66bfd46494134d20dba4af Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 4 Jan 2021 09:57:27 +0100 Subject: [PATCH 8/9] Update patterns/typestate.md --- patterns/typestate.md | 1 - 1 file changed, 1 deletion(-) diff --git a/patterns/typestate.md b/patterns/typestate.md index 5179f62a..cda3ade9 100644 --- a/patterns/typestate.md +++ b/patterns/typestate.md @@ -2,7 +2,6 @@ ## Description -(from http://cliffle.com/blog/rust-typestate/): The typestate pattern is an API design pattern that encodes information about an object's run-time state in its compile-time type. In particular, an API using the typestate pattern will have: From 62e211074483303cc9f61658d0993ad18c59d70d Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 4 Jan 2021 09:57:37 +0100 Subject: [PATCH 9/9] Update SUMMARY.md --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index 165e6cda..547740b7 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -24,9 +24,9 @@ - [Newtype](./patterns/newtype.md) - [RAII Guards](./patterns/RAII.md) - [Prefer Small Crates](./patterns/small-crates.md) + - [Typestate/Session Types](./patterns/typestate.md) - [Contain unsafety in small modules](./patterns/unsafe-mods.md) - [Visitor](./patterns/visitor.md) - - [Typestate/Session Types](./patterns/typestate.md) - [Anti-patterns](./anti_patterns/README.md) - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md)