Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Created October 19, 2020 20:25
Show Gist options
  • Save AnthonyMikh/891a7b423e771064efa40cba6e9e03af to your computer and use it in GitHub Desktop.
Save AnthonyMikh/891a7b423e771064efa40cba6e9e03af to your computer and use it in GitHub Desktop.
(Ab)using custom Deref implementations
use std::ops::{Deref, DerefMut};
use std::cell::Cell;
#[derive(Default)]
struct Simple {
field: u32,
}
impl Simple {
fn new(field: u32) -> Self {
Self { field }
}
}
#[derive(Clone, Copy)]
enum Choice {
First,
Second,
Third,
}
impl Choice {
fn next(self) -> Self {
use Choice::*;
match self {
First => Second,
Second => Third,
Third => First,
}
}
}
struct Tricky {
choice: Cell<Choice>,
first: Simple,
second: Simple,
third: Simple,
}
impl Tricky {
fn new(a: u32, b: u32, c: u32) -> Self {
Self {
choice: Cell::new(Choice::First),
first: Simple::new(a),
second: Simple::new(b),
third: Simple::new(c),
}
}
}
impl Default for Tricky {
fn default() -> Self {
Self::new(7, 4, 13)
}
}
impl Deref for Tricky {
type Target = Simple;
fn deref(&self) -> &Self::Target {
let choice = self.choice.get();
self.choice.set(choice.next());
match choice {
Choice::First => &self.first,
Choice::Second => &self.second,
Choice::Third => &self.third,
}
}
}
#[derive(Default)]
struct NiceTry {
first: Simple,
second: Simple,
}
impl Deref for NiceTry {
type Target = Simple;
fn deref(&self) -> &Self::Target {
&self.first
}
}
impl DerefMut for NiceTry {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.second
}
}
#[derive(Default)]
struct Unstable(Simple);
impl Deref for Unstable {
type Target = Simple;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Unstable {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.field += 10;
&mut self.0
}
}
fn main() {
let tricky = <Tricky as Default>::default();
assert_eq!(tricky.field, 7);
assert_eq!(tricky.field, 4);
assert_eq!(tricky.field, 13);
let mut nice_try = <NiceTry as Default>::default();
assert_eq!(nice_try.field, 0);
nice_try.field += 12;
assert_eq!(nice_try.field, 0);
let (mut a, mut b) = <(Unstable, Unstable) as Default>::default();
assert_eq!((a.field, b.field), (0, 0));
std::mem::swap(&mut a.field, &mut b.field);
assert_eq!((a.field, b.field), (10, 10));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment