https://github.com/Kobzol/rust-delegate
Rust method delegation with less boilerplate
https://github.com/Kobzol/rust-delegate
delegation rust rust-library rust-macro
Last synced: about 1 year ago
JSON representation
Rust method delegation with less boilerplate
- Host: GitHub
- URL: https://github.com/Kobzol/rust-delegate
- Owner: Kobzol
- License: apache-2.0
- Created: 2018-05-18T03:09:12.000Z (about 8 years ago)
- Default Branch: main
- Last Pushed: 2024-10-09T21:43:13.000Z (over 1 year ago)
- Last Synced: 2024-10-29T15:12:54.526Z (over 1 year ago)
- Topics: delegation, rust, rust-library, rust-macro
- Language: Rust
- Homepage: https://crates.io/crates/delegate
- Size: 118 KB
- Stars: 445
- Watchers: 9
- Forks: 18
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# Method delegation with less boilerplate
[](https://github.com/kobzol/rust-delegate/actions)
[](https://crates.io/crates/delegate)
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.
```rust
use delegate::delegate;
#[derive(Clone, Debug)]
struct Stack {
inner: Vec,
}
impl Stack {
pub fn new() -> Self {
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;
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
```rust
struct Stack {
inner: Vec
}
impl Stack {
delegate! {
to self.inner {
#[call(push)]
pub fn add(&mut self, value: u32);
}
}
}
```
### Use an arbitrary inner field expression
```rust
struct Wrapper {
inner: Rc>>
}
impl Wrapper {
delegate! {
to self.inner.deref().borrow_mut() {
pub fn push(&mut self, val: u32);
}
}
}
```
### Delegate to enum variants
```rust
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
```rust
use delegate::delegate;
struct Inner;
impl Inner {
pub fn method(&self, num: u32) -> u32 { num }
pub fn method_res(&self, num: u32) -> Result { 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;
// 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;
}
}
}
```
### Custom called expression
The `#[expr()]` attribute can be used to modify the delegated call. You can use the `$` sigil as a placeholder for what delegate would normally expand to, and wrap that expression with custom code.
_Note:_ the `$` placeholder isn't required and can be present multiple times if you want.
```rust
struct A(Vec);
impl A {
delegate! {
to self.0 {
#[expr(*$.unwrap())]
/// Here `$` == `self.0.get(idx)`
/// Will expand to `*self.0.get(idx).unwrap()`
fn get(&self, idx: usize) -> u8;
#[call(get)]
#[expr($?.checked_pow(2))]
/// Here `$` == `self.0.get(idx)`
/// Will expand to `self.0.get(idx)?.checked_pow(2)`
fn get_checked_pow_2(&self, idx: usize) -> Option;
}
}
}
```
### Add additional arguments to method
```rust
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,
}
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
```rust
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
pub async fn method(&self, num: u32) -> u32;
// calls method(num).await.into(), returns impl Future
#[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
```rust
struct MultiStack {
left: Vec,
right: Vec,
}
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);
}
}
}
```
### Inline attributes
`rust-delegate` inserts `#[inline(always)]` automatically. You can override that decision by specifying `#[inline]`
manually on the delegated method.
### Segment attributes
You can use an attribute on a whole delegation segment to automatically apply it to all methods in that segment:
```rust
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()
}
}
}
```
### Adding additional arguments
You can specify expressions in the signature that will be used as delegated arguments:
```rust
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 ;
}
}
}
```
### Parameter modifiers
You can 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.
- `#[newtype]`: Accesses the first tuple element (`.0`) of the parameter passed to the delegated method.
> Note that these modifiers might be removed in the future, try to use the more general `#[expr]` mechanism to achieve this functionality.
```rust
use delegate::delegate;
struct InnerType {}
impl InnerType {
fn foo(&self, other: Self) {}
}
impl From 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);
// Calls `self.0.bar(other.0);`
pub fn bar(&self, #[newtype] other: Self);
}
}
}
```
### Delegate associated functions
```rust
use delegate::delegate;
struct A {}
impl A {
fn foo(a: u32) -> u32 {
a + 1
}
}
struct B;
impl B {
delegate! {
to A {
fn foo(a: u32) -> u32;
}
}
}
assert_eq!(B::foo(1), 2);
```
### Delegate associated constants
```rust
use delegate::delegate;
trait WithConst {
const TOTO: u8;
}
struct A;
impl WithConst for A {
const TOTO: u8 = 1;
}
struct B;
impl WithConst for B {
const TOTO: u8 = 2;
}
struct C;
impl WithConst for C {
const TOTO: u8 = 2;
}
enum Enum {
A(A),
B(B),
C(C),
}
impl Enum {
delegate! {
to match self {
Self::A(a) => a,
Self::B(b) => b,
Self::C(c) => { println!("hello from c"); c },
} {
#[const(WithConst::TOTO)]
fn get_toto(&self) -> u8;
}
}
}
assert_eq!(Enum::A(A).get_toto(), ::TOTO);
```
## License
Licensed under either of
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
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`](./Cargo.toml).
[Rust Code of Conduct]: https://www.rust-lang.org/conduct.html