• Stars
    star
    388
  • Rank 110,734 (Top 3 %)
  • Language
    Rust
  • License
    Apache License 2.0
  • Created over 6 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Rust method delegation with less boilerplate

Method delegation with less boilerplate

Build Status Crates.io

This crate removes some boilerplate for structs that simply delegate some of their methods to one or more of their fields.

It gives you the delegate! macro, which delegates method calls to selected expressions (usually inner fields).

Example:

A Stack data structure implemented using an inner Vec via delegation.

use delegate::delegate;

#[derive(Clone, Debug)]
struct Stack<T> {
    inner: Vec<T>,
}
impl<T> Stack<T> {
    pub fn new() -> Self<T> {
        Self { inner: vec![] }
    }

    delegate! {
        to self.inner {
            pub fn is_empty(&self) -> bool;
            pub fn push(&mut self, value: T);
            pub fn pop(&mut self) -> Option<T>;
            pub fn clear(&mut self);

            #[call(len)]
            pub fn size(&self) -> usize;

            #[call(last)]
            pub fn peek(&self) -> Option<&T>;

        }
    }
}

Features:

  • Delegate to a method with a different name
struct Stack { inner: Vec<u32> }
impl Stack {
    delegate! {
        to self.inner {
            #[call(push)]
            pub fn add(&mut self, value: u32);
        }
    }
}
  • Use an arbitrary inner field expression
struct Wrapper { inner: Rc<RefCell<Vec<u32>>> }
impl Wrapper {
    delegate! {
        to self.inner.deref().borrow_mut() {
            pub fn push(&mut self, val: u32);
        }
    }
}
  • Delegate to enum variants
use delegate::delegate;

enum Enum {
    A(A),
    B(B),
    C { v: C },
}

struct A {
    val: usize,
}

impl A {
    fn dbg_inner(&self) -> usize {
        dbg!(self.val);
        1
    }
}
struct B {
    val_a: String,
}

impl B {
    fn dbg_inner(&self) -> usize {
        dbg!(self.val_a.clone());
        2
    }
}

struct C {
    val_c: f64,
}

impl C {
    fn dbg_inner(&self) -> usize {
        dbg!(self.val_c);
        3
    }
}

impl Enum {
    delegate! {
        // transformed to
        //
        // ```rust
        // match self {
        //     Enum::A(a) => a.dbg_inner(),
        //     Enum::B(b) => { println!("i am b"); b }.dbg_inner(),
        //     Enum::C { v: c } => { c }.dbg_inner(),
        // }
        // ```
        to match self {
            Enum::A(a) => a,
            Enum::B(b) => { println!("i am b"); b },
            Enum::C { v: c } => { c },
        } {
            fn dbg_inner(&self) -> usize;
        }
    }
}
  • Use modifiers that alter the generated method body
use delegate::delegate;
struct Inner;
impl Inner {
    pub fn method(&self, num: u32) -> u32 { num }
    pub fn method_res(&self, num: u32) -> Result<u32, ()> { Ok(num) }
}
struct Wrapper { inner: Inner }
impl Wrapper {
    delegate! {
        to self.inner {
            // calls method, converts result to u64 using `From`
            #[into]
            pub fn method(&self, num: u32) -> u64;

            // calls method, returns ()
            #[call(method)]
            pub fn method_noreturn(&self, num: u32);

            // calls method, converts result to i6 using `TryFrom`
            #[try_into]
            #[call(method)]
            pub fn method2(&self, num: u32) -> Result<u16, std::num::TryFromIntError>;

            // calls method_res, unwraps the result
            #[unwrap]
            pub fn method_res(&self, num: u32) -> u32;

            // calls method_res, unwraps the result, then calls into
            #[unwrap]
            #[into]
            #[call(method_res)]
            pub fn method_res_into(&self, num: u32) -> u64;

            // specify explicit type for into
            #[into(u64)]
            #[call(method)]
            pub fn method_into_explicit(&self, num: u32) -> u64;
        }
    }
}
  • Add additional arguments to method
struct Inner(u32);
impl Inner {
    pub fn new(m: u32) -> Self {
        // some "very complex" constructing work
        Self(m)
    }
    pub fn method(&self, n: u32) -> u32 {
        self.0 + n
    }
}

struct Wrapper {
    inner: OnceCell<Inner>,
}

impl Wrapper {
    pub fn new() -> Self {
        Self {
            inner: OnceCell::new(),
        }
    }
    fn content(&self, val: u32) -> &Inner {
        self.inner.get_or_init(|| Inner(val))
    }
    delegate! {
        to |k: u32| self.content(k) {
            // `wrapper.method(k, num)` will call `self.content(k).method(num)`
            pub fn method(&self, num: u32) -> u32;
        }
    }
}
  • Call await on async functions
struct Inner;
impl Inner {
    pub async fn method(&self, num: u32) -> u32 { num }
}
struct Wrapper { inner: Inner }
impl Wrapper {
    delegate! {
        to self.inner {
            // calls method(num).await, returns impl Future<Output = u32>
            pub async fn method(&self, num: u32) -> u32;
            // calls method(num).await.into(), returns impl Future<Output = u64>
            #[into]
            #[call(method)]
            pub async fn method_into(&self, num: u32) -> u64;
        }
    }
}

You can use the #[await(true/false)] attribute on delegated methods to specify if .await should be generated after the delegated expression. It will be generated by default if the delegated method is async.

  • Delegate to multiple fields
struct MultiStack {
    left: Vec<u32>,
    right: Vec<u32>,
}
impl MultiStack {
    delegate! {
        to self.left {
            /// Push an item to the top of the left stack
            #[call(push)]
            pub fn push_left(&mut self, value: u32);
        }
        to self.right {
            /// Push an item to the top of the right stack
            #[call(push)]
            pub fn push_right(&mut self, value: u32);
        }
    }
}
  • Inserts #[inline(always)] automatically (unless you specify #[inline] manually on the method)
  • You can use an attribute on a whole segment to automatically apply it to all methods in that segment:
struct Wrapper { inner: Inner }

impl Wrapper {
 delegate! {
   #[unwrap]
   to self.inner {
     fn foo(&self) -> u32; // calls self.inner.foo().unwrap()
     fn bar(&self) -> u32; // calls self.inner.bar().unwrap()
   }
 }
}
  • Specify expressions in the signature that will be used as delegated arguments
use delegate::delegate;
struct Inner;
impl Inner {
    pub fn polynomial(&self, a: i32, x: i32, b: i32, y: i32, c: i32) -> i32 {
        a + x * x + b * y + c
    }
}
struct Wrapper { inner: Inner, a: i32, b: i32, c: i32 }
impl Wrapper {
    delegate! {
        to self.inner {
            // Calls `polynomial` on `inner` with `self.a`, `self.b` and
            // `self.c` passed as arguments `a`, `b`, and `c`, effectively
            // calling `polynomial(self.a, x, self.b, y, self.c)`.
            pub fn polynomial(&self, [ self.a ], x: i32, [ self.b ], y: i32, [ self.c ]) -> i32 ;
            // Calls `polynomial` on `inner` with `0`s passed for arguments
            // `a` and `x`, and `self.b` and `self.c` for `b` and `c`,
            // effectively calling `polynomial(0, 0, self.b, y, self.c)`.
            #[call(polynomial)]
            pub fn linear(&self, [ 0 ], [ 0 ], [ self.b ], y: i32, [ self.c ]) -> i32 ;
        }
    }
}
  • Modify how will an input parameter be passed to the delegated method with parameter attribute modifiers. Currently, the following modifiers are supported:
    • #[into]: Calls .into() on the parameter passed to the delegated method.
    • #[as_ref]: Calls .as_ref() on the parameter passed to the delegated method.
use delegate::delegate;
//!
struct InnerType {}
impl InnerType {
    fn foo(&self, other: Self) {}
}
//!
impl From<Wrapper> for InnerType {
    fn from(wrapper: Wrapper) -> Self {
        wrapper.0
    }
}
//!
struct Wrapper(InnerType);
impl Wrapper {
    delegate! {
        to self.0 {
            // Calls `self.0.foo(other.into());`
            pub fn foo(&self, #[into] other: Self);
        }
    }
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Conduct

Please follow the Rust Code of Conduct. For escalation or moderation issues please contact the crate author(s) listed in Cargo.toml.

More Repositories

1

hardware-effects

Demonstration of various hardware effects.
C++
2,715
star
2

cargo-wizard

Cargo subcommand for configuring Cargo projects for best performance.
Rust
710
star
3

cargo-pgo

Cargo subcommand for optimizing Rust binaries/libraries with PGO and BOLT.
Rust
508
star
4

hardware-effects-gpu

Demonstration of various hardware effects on CUDA GPUs.
C++
304
star
5

cargo-remark

Cargo subcommand for viewing LLVM optimization remarks.
Rust
162
star
6

davis

Assembly debugger written in Angular 2.
TypeScript
56
star
7

rust-course-fei

Rust course taught at FEI VŠB-TUO.
Rust
13
star
8

sigmod-2018

Code for the SIGMOD 2018 programming contest. Finished at 2nd place.
C++
12
star
9

debug-visualizer

Program memory visualizer for GDB/LLDB (bachelor thesis)
Python
10
star
10

sigmod-2019

Code for the SIGMOD 2019 programming contest. Finished at 2nd place.
C++
8
star
11

llvm-instrument

LLVM instrumentation
C++
6
star
12

rustlang.cz

Web that gathers information about the Rust community in the Czech Republic.
HTML
6
star
13

advent-of-code

Advent of code solutions
Python
4
star
14

cuda-profile

Instrumentation based profiler for CUDA (master thesis)
C++
3
star
15

sigmod-2016

Code for the SIGMOD 2016 programming contest. Finished at 14th place.
C++
3
star
16

talks

Source code and slides for my public talks.
Python
3
star
17

cfggen

Python configuration generator
Python
3
star
18

llvm-se

Static analysis using symbolic execution on top of LLVM IR
C++
2
star
19

handmade-quake

Quake recreated by following the tutorial from Philip Buuck (https://www.youtube.com/channel/UCXgjH2-Mrb3-h1_iWurz7dQ).
C
2
star
20

kobzol

2
star
21

async-iterator-examples

Examples of Rust async iterators
Rust
2
star
22

kobzol.github.io

Blog about programming stuff.
HTML
2
star
23

rust-web-app-demo

Demo of a small newsletter web app in Rust.
Rust
2
star
24

Spaceships

Android (Java) 2D game project made as a school assignment.
Java
1
star
25

Ghrab-Robot

Projekt robotického kroužku Gymnázia Ostrava-Hrabůvka.
C
1
star
26

cuda-graph

BFS implemented in CUDA.
C++
1
star
27

agu

Algorithmisation of Geometrical Problems VSB-TUO course
C++
1
star
28

ZPG-project

Project for ZPG (Principles of Computer Graphics).
C
1
star
29

turret

School project, (somehow modified) clone of Tower defense.
Java
1
star
30

Computer-Graphics-I

Code for subject Computer Graphics I at VSB-TUO.
C++
1
star
31

sigmod-2017

Code for the SIGMOD 2017 programming contest. Finished at 15th place.
C++
1
star
32

valgrind-se

Symbolic execution in Valgrind. Based on https://github.com/spirali/aislinn.
C
1
star
33

elsie-gallery

Python
1
star
34

arduino-tetris

Classic tetris game displayed on 8x8 LED Matrix (MAX72xx) on Arduino
C++
1
star
35

mkdocs-nedoc-plugin

Mkdocs plugin for the nedoc Python API documentation generator.
Python
1
star
36

rust-cmd-spawn-bench

Benchmark for process spawning in Rust, on Linux.
Python
1
star
37

pyladies-extended

Jupyter Notebook
1
star