Skip to content

Instantly share code, notes, and snippets.

@Nekrolm
Created November 15, 2025 15:53
Show Gist options
  • Select an option

  • Save Nekrolm/b840fb23209fe06bd7b392636123016e to your computer and use it in GitHub Desktop.

Select an option

Save Nekrolm/b840fb23209fe06bd7b392636123016e to your computer and use it in GitHub Desktop.
rust types safe state transitions example
use anyhow; // 1.0.100
use anyhow::Context;
use anyhow::Result;
use std::fmt::Write;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::process::abort;
// struct ScannerOpenParams {};
/* Correct usage:
* auto scanner = Scanner(...);
* scanner.configure_connection();
* scanner.connect();
* scanner.set_scan_parameters();
* scanner.start()
* for (int i = 0; i < 5; ++i) {
* scanner.scan();
* }
* scanner.stop();
*/
// struct Scanner {
// Scanner(ScannerOpenParams) {}
// void configure_connection() {};
// void connect() {};
// void set_scan_parameters() {};
// void start() {};
// void scan() {};
// void stop() {};
// };
struct ScannerInnerImpl {
connected: bool,
started: bool,
config: String,
connection_config: String,
line: i32,
}
struct ScannerOpenParams {
model_version: i32,
}
#[derive(Debug)]
struct ScanParameters {
exposure: u32,
fov: u32,
}
impl ScannerInnerImpl {
fn open(params: ScannerOpenParams) -> Result<Self> {
anyhow::ensure!(params.model_version <= 42, "unsupported version");
Ok(Self {
connected: false,
started: false,
config: String::new(),
connection_config: String::new(),
line: 0,
})
}
fn configure_connection(&mut self, uri: &str) -> Result<()> {
anyhow::ensure!(
!self.connected,
"cannot configure already connected scanner"
);
anyhow::ensure!(uri.starts_with("scanner://"), "unknown scanner uri");
self.connection_config.clear();
self.connection_config.push_str(uri);
Ok(())
}
fn connect(&mut self) -> Result<()> {
anyhow::ensure!(!self.connected, "already connected");
anyhow::ensure!(
!self.connection_config.is_empty(),
"connection is not confugured"
);
println!("connecting scanner {}", self.connection_config);
self.connected = true;
Ok(())
}
fn disconnect(&mut self) {
if !self.connected {
println!("WARN: scanner is not connected");
};
self.connected = false;
}
fn configure_scanner(&mut self, config: ScanParameters) -> Result<()> {
anyhow::ensure!(self.connected, "cannot configure unconnected scanner");
anyhow::ensure!(!self.started, "cannot configure started scanner");
anyhow::ensure!(
config.exposure < 600000,
"too long exposure is not supported"
);
anyhow::ensure!(config.fov < 180, "too wide fov is not supported");
self.config.clear();
write!(&mut self.config, "{config:?}")?;
Ok(())
}
fn start(&mut self) -> Result<()> {
anyhow::ensure!(self.connected, "cannot start unconnected scanner");
anyhow::ensure!(!self.config.is_empty(), "scanner is not confugured");
println!("starting scanner with params {}", self.connection_config);
self.started = true;
self.line = 0;
Ok(())
}
fn scan(&mut self) -> Result<Vec<i32>> {
anyhow::ensure!(self.started, "cannot scan: scanner not started");
self.line += 1;
Ok((1..=16).map(|x| x * self.line).collect())
}
fn stop(&mut self) {
if !self.started {
println!("WARN: Scanner is already stopped");
}
self.started = false;
}
}
impl Drop for ScannerInnerImpl {
fn drop(&mut self) {
if self.started {
println!("WARN: dropping scanner while it is working!!!!!");
self.stop();
}
if self.connected {
println!("Disconnecting: {}", self.connection_config);
self.connected = false;
}
}
}
struct Scanner {
inner: OnDropGuard<ScannerInnerImpl, Scanner>,
}
struct ConnectedScanner {
inner: OnDropGuard<ScannerInnerImpl, ConnectedScanner>,
}
struct StartedScanner {
inner: OnDropGuard<ScannerInnerImpl, StartedScanner>,
}
impl DropBehavior<ScannerInnerImpl> for Scanner {
fn on_drop(_: ScannerInnerImpl) {}
}
impl DropBehavior<ScannerInnerImpl> for ConnectedScanner {
fn on_drop(_: ScannerInnerImpl) {
println!("DONT LEAVE SCANNER IN CONNECTED STATE");
abort()
}
}
impl DropBehavior<ScannerInnerImpl> for StartedScanner {
fn on_drop(_: ScannerInnerImpl) {
println!("DONT LEAVE SCANNER IN STARTED STATE");
abort()
}
}
impl Scanner {
fn open(params: ScannerOpenParams) -> Result<Self> {
Ok(Self {
inner: ScannerInnerImpl::open(params)?.into(),
})
}
fn configure_connection(&mut self, uri: &str) -> Result<()> {
self.inner.configure_connection(uri)
}
fn connect(self) -> Result<ConnectedScanner> {
let mut inner = self.inner;
inner.connect()?;
Ok(ConnectedScanner {
inner: inner.rebind(),
})
}
}
impl ConnectedScanner {
fn configure(&mut self, config: ScanParameters) -> Result<()> {
self.inner.configure_scanner(config)
}
fn start(mut self) -> Result<StartedScanner> {
self.inner.start()?;
Ok(StartedScanner {
inner: self.inner.rebind(),
})
}
fn disconnect(mut self) -> Scanner {
self.inner.disconnect();
Scanner {
inner: self.inner.rebind(),
}
}
}
impl StartedScanner {
fn scan(&mut self) -> Result<Vec<i32>> {
self.inner.scan()
}
fn stop(mut self) -> ConnectedScanner {
self.inner.stop();
ConnectedScanner {
inner: self.inner.rebind(),
}
}
}
trait DropBehavior<T> {
#[inline(always)]
fn on_drop(_: T) {}
}
struct OnDropGuard<T, S: DropBehavior<T>> {
inner: ManuallyDrop<T>,
_behavior: PhantomData<S>,
}
impl<T, S> From<T> for OnDropGuard<T, S>
where
S: DropBehavior<T>,
{
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T, S> OnDropGuard<T, S>
where
S: DropBehavior<T>,
{
fn new(value: T) -> Self {
Self {
inner: ManuallyDrop::new(value),
_behavior: Default::default(),
}
}
fn into_inner(self) -> T {
let mut guard = ManuallyDrop::new(self);
unsafe { ManuallyDrop::take(&mut guard.inner) }
}
fn rebind<U: DropBehavior<T>>(self) -> OnDropGuard<T, U> {
OnDropGuard::new(self.into_inner())
}
}
impl<T, S> Deref for OnDropGuard<T, S>
where
S: DropBehavior<T>,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T, S> DerefMut for OnDropGuard<T, S>
where
S: DropBehavior<T>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T, S> Drop for OnDropGuard<T, S>
where
S: DropBehavior<T>,
{
fn drop(&mut self) {
S::on_drop(unsafe { ManuallyDrop::take(&mut self.inner) })
}
}
enum MyScannerImpl {
Base(Scanner),
Connected(ConnectedScanner),
Started(StartedScanner),
}
struct MyScanner(Option<MyScannerImpl>);
trait DynScanner {
fn configure_connection(&mut self, uri: &str) -> Result<()>;
fn connect(&mut self) -> Result<()>;
fn disconnect(&mut self) -> Result<()>;
fn configure_scanner(&mut self, config: ScanParameters) -> Result<()>;
fn start(&mut self) -> Result<()>;
fn scan(&mut self) -> Result<Vec<i32>>;
fn stop(&mut self) -> Result<()>;
}
// fn mutate_with<T>(val: &mut T, mutator: impl FnOnce(T) -> T) {
// let new_val = match std::panic::catch_unwind(AssertUnwindSafe(|| {
// mutator(unsafe { std::ptr::read(val) })
// })) {
// Ok(val) => val,
// Err(_) => abort(),
// };
// unsafe { std::ptr::write(val, new_val) };
// }
impl MyScanner {
fn open(params: ScannerOpenParams) -> Result<Self> {
Scanner::open(params)
.map(MyScannerImpl::Base)
.map(Some)
.map(Self)
}
}
impl DynScanner for MyScanner {
fn configure_connection(&mut self, uri: &str) -> Result<()> {
let state = self.0.as_mut().context("Broken state")?;
let MyScannerImpl::Base(scanner) = state else {
anyhow::bail!("Cannot configure connection not in base state");
};
scanner.configure_connection(uri)?;
Ok(())
}
fn connect(&mut self) -> Result<()> {
let state = self.0.take().context("Broken state")?;
let MyScannerImpl::Base(scanner) = state else {
anyhow::bail!("Cannot connect not in base state");
};
let connected = scanner.connect()?;
let _ = self.0.insert(MyScannerImpl::Connected(connected));
Ok(())
}
fn disconnect(&mut self) -> Result<()> {
let state = self.0.take().context("Broken state")?;
let MyScannerImpl::Connected(scanner) = state else {
anyhow::bail!("Cannot disconnect not in connected state");
};
let connected = scanner.disconnect();
let _ = self.0.insert(MyScannerImpl::Base(connected));
Ok(())
}
fn configure_scanner(&mut self, config: ScanParameters) -> Result<()> {
let state = self.0.as_mut().context("Broken state")?;
let MyScannerImpl::Connected(scanner) = state else {
anyhow::bail!("Cannot configure scanner not in connected state");
};
scanner.configure(config)
}
fn start(&mut self) -> Result<()> {
let state = self.0.take().context("Broken state")?;
let MyScannerImpl::Connected(scanner) = state else {
anyhow::bail!("Cannot start not in connected state");
};
let connected = scanner.start()?;
let _ = self.0.insert(MyScannerImpl::Started(connected));
Ok(())
}
fn scan(&mut self) -> Result<Vec<i32>> {
let state = self.0.as_mut().context("Broken state")?;
let MyScannerImpl::Started(scanner) = state else {
anyhow::bail!("Cannot scan not in started state");
};
scanner.scan()
}
fn stop(&mut self) -> Result<()> {
let state = self.0.take().context("Broken state")?;
let MyScannerImpl::Started(scanner) = state else {
anyhow::bail!("Cannot stop not in started state");
};
let connected = scanner.stop();
let _ = self.0.insert(MyScannerImpl::Connected(connected));
Ok(())
}
}
fn open_scanner() -> Result<Box<dyn DynScanner>> {
let scanner = MyScanner::open(ScannerOpenParams { model_version: 33 })?;
Ok(Box::new(scanner))
}
fn main() -> Result<()> {
let mut scanners = Vec::<Box<dyn DynScanner>>::new();
for _ in 0..5 {
scanners.push(
open_scanner()?
);
}
for scanner in &mut scanners {
scanner.configure_connection("scanner://ololo")?;
scanner.connect()?;
scanner.configure_scanner(ScanParameters { exposure: 55, fov: 33 })?;
scanner.start()?;
}
for _ in 0..5 {
for scanner in &mut scanners {
println!("{:?}", scanner.scan());
}
}
for mut scanner in scanners {
scanner.stop()?;
scanner.disconnect()?;
}
Ok(())
// let mut scanner = Scanner::open(ScannerOpenParams { model_version: 33 })?;
// scanner.configure_connection("scanner://best-scanner")?;
// let mut connected_scanner = scanner.connect()?;
// connected_scanner.configure(ScanParameters {
// exposure: 10,
// fov: 90,
// })?;
// let mut ready_scanner = connected_scanner.start()?;
// for _ in 0..5 {
// let data = ready_scanner.scan()?;
// println!("{data:?}");
// }
// ready_scanner.stop().disconnect();
// Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment