Giter Club home page Giter Club logo

rust-delegate's Introduction

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.
    • #[newtype]: Accesses the first tuple element (.0) of 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);
                // Calls `self.0.bar(other.0);`
                pub fn bar(&self, #[newtype] 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.

rust-delegate's People

Contributors

abreis avatar capoferro avatar chancancode avatar dansnow avatar emoun avatar gfreezy avatar habnabit avatar knutwalker avatar kobzol avatar kondziu avatar longfangsong avatar ruifengx avatar techassi avatar trueegorletov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-delegate's Issues

Arbitrary inner field expressions

The readme says:

Use an arbitrary inner field expression

But this fails:

	delegate! {
		to self.pass_output_textures[self.image_pass_idx] {
			pub fn input_texture(&self) -> &Texture2d;
		}
	}

error: proc macro panicked
help: message: Use a field expression to select delegator (e.g. self.inner)

What's the right way to do this? :)

New release?

Hi! Could we get a new release to include #55? That would be appreciated :)

Also, I notice that the latest version on crates.io is 0.10.0, but Git only has version 0.9.0 - is that intentional?

Transfer ownership!

Since I am not really involved in maintaining this crate anymore, I wonder if any of the current maintainers would be interested in me transferring over the GitHub ownership (or make an org for it)? I don’t mind keeping it under my user, but if there is any interest just let me know and it will be happy to do it.

Allow #[from] and #[into] annotations, pattern-matching or even arbitrary transformation functions for delegated arguments

Lately, I found myself attempting to delegate some trait implementations in a newtype to the inner argument, however found myself in a bit of a pickle:

use delegate::delegate;
struct MyNewU32(u32);

impl std::cmp::PartialEq for MyNewU32 {
    delegate!{
        to self.0 {
            fn eq(&self, other: &Self) -> bool;
        }
    }
}

While I understand the PartialEq trait has a derive macro available for it, I use it only as an example, as I imagine not all traits have a derive macro available that works on newtypes, and similar patterns might exist elsewhere requiring an argument whose type has Self in it, such as &Self, Box<Self>, or even Vec<Self>.

With this in mind, I found myself in a bit of a pickle.
AFAIK, the delegate!{} implementation allows the self argument to be any arbitrary expression (such as self.0 in this case), and the return value of delegated methods can also be wrapped in a from() call if necessary with the #[from] annotation, and even the arguments supplied to the "delegatee" method can also be an arbitrary expression, but I have still to find a way to supply the other: &Self argument in this case.

As I see it (other options are more than welcome, if there are any), there are three-or-four possible ways to accommodate this use case:

  • Allow pattern-matching for the delegated argument
    • Something like fn eq(&self, MyNewU32(other): &Self) -> bool;
  • Allow #[from] annotations for the delegated argument
    • Assuming an impl existed for From<&MyNewU32> for &u32, one could write fn eq(&self, #[from] other: &Self) -> bool;, or similar, perhaps specifying both the converted-from type and the converted-to type somewhere in the annotation, argument name/pattern, or argument type.
      • This could be done automatically for all arguments perhaps, considering From<T> for <T> is a thing
  • Allow arbitrary transformation closures to be provided for each argument, or the entire range of arguments, before passing those arguments into the "delegatee" function.
    • For a single argument: fn eq(&self, |other: &Self| other.0 ) -> bool;
    • For the entire function (essentially equivalent to not using delegate at all, but may be preferred for stylistic purposes, if it's just one exception among many delegated methods)
      fn eq(&self,other: &Self) -> bool { delegatee(self.0,other.0) }

One, none, or many of these proposals could be implemented, with or without modifications. I just think this might be useful to make the delegate macro a bit more flexible, and better bridge the gap between implementing a custom method manually, and delegating as-is with no changes. In particular, I think some of these proposals may be useful when dealing with newtypes, which I presume is one of the main goals of this project.

Doc: rewrite README

The initial README looks like this is a crate that implements a LIFO stack. It should be rewritten to have less duplication, clearly show what the crate does and enumerates all the optional features (since there's no other public API at this moment, as it is a macro crate).

Suggestion: Shorter attribute name than target_method

Just as with target, target_method is quite long to type, and it becomes noticeable when using the delegate macro a lot.
Could we shorten it to make it even more convenient? :)
E.g. how about using as instead?

    delegate! {
        to self.inner {
            /// The number of items in the stack
            #[as(len)]
            pub fn size(&self) -> usize;

If you don't like as, other alternatives (in increasing length) are #[call(len)], #[calls(len)] and #[rename(len)].
Even though the word "as" in itself is not as descriptive as something like "call", in this context I think it's not a problem because this is the only attribute available in this context and its meaning can be inferred from seeing that we're in a delegate! { block.

Suggestion: Shorter keyword than `target`

target is quite long to type, and it becomes noticeable when using the delegate macro a lot.
Could we shorten it for the next version of this crate? :)
E.g. how about using to as keyword, it also reads nicely, like "delegate to self.foo":

impl<S: Selectable> Switcher2<S> {
    delegate! {
        to self.switcher {
            pub fn page(&self) -> S;

            pub fn set_col(&mut self, col: Col);

            pub fn set_selected_brightness(&mut self, b: f32);
        }
    }
}

If you don't like to, how about for? It reads less like an English sentence compared to to, but has the benefit of getting syntax-highlighted in editors, and looks nice/familiar because it opens a block:

impl<S: Selectable> Switcher2<S> {
    delegate! {
        for self.switcher {
            pub fn page(&self) -> S;

            pub fn set_col(&mut self, col: Col);

            pub fn set_selected_brightness(&mut self, b: f32);
        }
    }
}

(Another possibility would be as but that already has a semantic connotation that could confuse readers in this context.)

is it possible to delegate all methods?

could I do something like this?

#[derive(Default, Debug)]
pub struct MetaData {
    data: String,
}

impl MetaData {
    fn metadata_contains(&self, s: &str) -> bool {
        self.data.contains(s)
    }
    fn set_metadata(&mut self, s: &str) {
        self.data = s.to_owned()
    }
}

#[derive(Default, Debug)]
pub struct Data {
    metadata: MetaData,
    data: Vec<u8>
}

impl Data {
    delegate! {
        to self.metadata {
             // without needing to write out
             // each of the methods defined on MetaData
        }
    }
}

So then you can do something like:

let mut data = Data::default();
data.set_metadata("");

Without needing to explicitly define set_metadata in the delegate! block of the Data impl.

Is something like this possible?

`delegate!` macro fails to parse parameters with trailing comma.

It seems that the delegate! macro fails to parse delegate functions when the parameters include a trailing comma. Here's an example:

impl Delegating {
    delegate! {
        target self.inner {
            pub fn complex_delegated_operation(
                source: SourceParameter,
                destination: DestinationParameter,
                data: Data, // Trailing comma.
            ) -> Result<Data, Error>;
        }
    }
}

This is the formatting used by rustfmt for functions that spill over column 100. Removing the trailing comma from data: Data, allows delegate! to parse, but this is standard Rust syntax and should probably be parsed without errors.

In the debug output, the last parameter is missing from signature_args.

Assign Return Value Of Delegated Calls To Delegated Field

I'm working on an API client that's implemented (essentially) as a newtype wrapper around a reqwest::Client. For convenience, I'd like to mirror reqwest's API as much as possible (no point in reinventing the wheel, right?) so I've also included a newtype wrapper for reqwest::ClientBuilder as well.

The client and builder wrappers basically look like this:

ClientBuilder

#[derive(Debug, Default)]
pub struct APIClientBuilder {
    api_key: Option<reqwest::header::HeaderValue>,
    builder: reqwest::ClientBuilder,
}

impl Deref for APIClientBuilder {
    type Target = reqwest::ClientBuilder;

    fn deref(&self) -> &Self::Target {
        &self.builder
    }
}

impl APIClientBuilder {
    pub fn new() -> Self {
        // ...
    }

    pub fn build(self) -> Result<APIClient, crate::errors::ClientError> {
        // ...
    }

    /// Set the API key to use for authenticating requests
    pub fn api_key<ApiKey: ToString>(mut self, api_key: ApiKey) -> Self {
        // ...
    }
}

APIClient

#[derive(Debug)]
pub struct APIClient(reqwest::Client);

impl Deref for APIClient {
    type Target = reqwest::Client;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl APIClient {
    /// Create a new [`APIClient`][APIClient] instance
    pub fn new<ApiKey: ToString>(api_key: ApiKey) -> Result<Self, crate::errors::APIClientError> {
        Self::builder()  // -> APIClientBuilder  // this is fine 
            .api_key(api_key)  // -> APIClientBuilder  // this is *also* fine
            .cookie_store(true)  // -> reqwest::ClientBuilder  // problems start here
            .timeout(Duration::from_secs(360))  // -> reqwest::ClientBuilder
            .connect_timeout(Duration::from_secs(360))  // -> reqwest::ClientBuilder
            .build()  // -> reqwest::Client  // this *should* be crate::client::APIClient
    }
}

I've annotated the code block above to illustrate the issue - the return type of the builder methods changes depending on whether or not they're "overridden" by the wrapper type.

My question is - would it be possible to instruct delegate to "capture" (re-assign) the return value of delegated calls back to the delegated field? At the moment, doing:

// ...

delegate! {
    to self.builder {
        pub fn timeout(mut self, timeout: Duration) -> APIClientBuilder;
        pub fn cookie_store(mut self, enable: bool) -> APIClientBuilder;
        pub fn connect_timeout(mut self, timeout: Duration) -> APIClientBuilder;
    }
}

// ...

causes delegate to generate:

// ...

#[inline(always)]
pub fn timeout(mut self, timeout: Duration) -> APIClientBuilder {  // incorrect return type
    self.builder.timeout(timeout)
}

#[inline(always)]
pub fn cookie_store(mut self, enable: bool) -> APIClientBuilder {  // incorrect return type
    self.builder.cookie_store(enable)
}

#[inline(always)]
pub fn connect_timeout(mut self, timeout: Duration) -> APIClientBuilder {  // incorrect return type
    self.builder.connect_timeout(timeout)
}

// ...

Ideally (and with the correct attribute / annotation presumably) though, it would generate:

// ...

#[inline(always)]
pub fn timeout(mut self, timeout: Duration) -> APIClientBuilder {
    self.builder = self.builder.timeout(timeout);
    self
}

#[inline(always)]
pub fn cookie_store(mut self, enable: bool) -> APIClientBuilder {
    self.builder = self.builder.cookie_store(enable);
    self
}

#[inline(always)]
pub fn connect_timeout(mut self, timeout: Duration) -> APIClientBuilder {
    self.builder = self.builder.connect_timeout(timeout);
    self
}

// ...

Suggestion: Allow non-field targets

Example:

use std::sync::Mutex;
use delegate::delegate;

#[derive(Clone, Debug)]
struct AtomicStack<T> {
    inner: Mutex<Vec<T>>,
}

impl<T> AtomicStack<T> {
    pub fn new() -> AtomicStack<T> {
        Stack { inner: Mutex::new(vec![]) }
    }

    delegate! {
        target self.inner.lock().unwrap() {
            /// The number of items in the stack
            #[target_method(len)]
            pub fn size(&self) -> usize;

            /// Whether the stack is empty
            pub fn is_empty(&self) -> bool;

            /// Push an item to the top of the stack
            pub fn push(&self, value: T);

            /// Remove an item from the top of the stack
            pub fn pop(&self) -> Option<T>;

            /// Accessing the top item without removing it from the stack
            #[target_method(last)]
            pub fn peek(&self) -> Option<&T>;

            /// Remove all items from the stack
            pub fn clear(&self);
        }
    }
}

Delegate to Self

I try to use delegate and I sometime need to delegate to a "static" method so I tried:

struct A { }
impl A {
    fn something(a: String) {

    }
}

struct B {
    a: A
}

impl B {
    delegate! {
        // the `Self` constructor can only be used with tuple or unit structs
        to (Self) {
            fn something(a: String);
        }
    }
}

But it gives me an error. Is that something that's not possible? Or does it not makes sense only to me?

Delegate target cannot come from macro expansion

Take the following code:

use delegate::delegate;

trait Trait{
  fn method_to_delegate(&self) -> bool;
}

struct Struct1();
impl Trait for Struct1 {
  fn method_to_delegate(&self) -> bool {
    true
  }
}

struct Struct2(pub Struct1);

// We use a macro to impl 'Trait' for 'Struct2' such that
// the target is a variable in the macro.
macro_rules! some_macro {
  ($delegate_to:expr) => {
    impl Trait for Struct2 {
      delegate!{
        // '$delegate' will expand to 'self.0' before ´delegate!' is expanded.
        to $delegate_to {
          fn method_to_delegate(&self) -> bool;
        }
      }
    }
  }
}
some_macro!{self.0}

This should work, as $delegate_to will expand to self.0 and is therefore trivially correct. However, it currently fails to compile with the error:

error: proc macro panicked
  --> tests\in_macro_expansion.rs:25:4
   |
25 |               delegate!{
   |  _____________^
26 | |                 // '$delegate' will expand to 'self.0' before ´delegate!' is expanded.
27 | |                 to $delegate_to {
28 | |                     fn method_to_delegate(&self) -> bool;
29 | |                 }
30 | |             }
   | |_____________^
...
34 |   some_macro!{self.0}
   |   ------------------- in this macro invocation
   |
   = help: message: Use a field expression to select delegator (e.g. self.inner)

error: aborting due to previous error

The reason it fails is that macros expand variables into a group, which is a type of expression not handled by delegate!.

I'm working on a PR with a fix.

0.5.0 fails to compile

I believe something happened around the syn crate, because the build fails with this reason:

   Updating crates.io index
   Compiling proc-macro2 v1.0.24
   Compiling unicode-xid v0.2.1
   Compiling syn v1.0.58
   Compiling quote v1.0.8
   Compiling delegate v0.5.0 (/home/mzs/.cargo/registry/src/github.com-1ecc6299db9ec823/delegate-0.5.0)
error[E0432]: unresolved import `syn::export`
  --> src/lib.rs:93:10
   |
93 | use syn::export::TokenStream2;
   |          ^^^^^^ could not find `export` in `syn`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
error: could not compile `delegate`

To learn more, run the command again with --verbose.

Also, could you enhance your CI with some periodic compilation?

Thanks a lot!

Delegate with additional arguments

Hi!

I have a use case for delegation with internal mutability with around 100 methods in the same object and rust-delegate would be very useful to halve the boilerplate code.

impl Database {
    delegate! {
        to self.data.borrow_mut() {
            pub fn project(&self, id: &ProjectId) -> Option<Project>;
            // ...
        }
   }
}   

However, the method on the inner object always carries an extra argument that should be passed in during delegation:

pub fn project(&mut self, id: &ProjectId, source: &Source) -> Option<Project> { /* ... */ }

It would be useful to have the an ability to append expressions to the argument list for the function called on the inner object, e.g.:

impl Database {
    delegate! {
        to self.data.borrow_mut() {
            #[extra_args(&self.source)]
            pub fn project(&self, id: &ProjectId) -> Option<Project>;
            // ...
        }
   }
}   

`no_std` support

Hello ! I really like this carte but I cannot use it in its current version due to the use or std: is it planned to support no_std as well (with feature flags for exemple) ?

Change default inlining mode

It seems that #[inline(always)] might not be an ideal inlining mode for short wrapper functions (that are produced by delegaŧe), because Rust often inlines bottom up. A safer option would be to default just to #[inline].

allow #[try_from] and #[try_into]

It is awesome to support TryFrom and TryInto.

In some case it is unsafe to just using from or into(especially there are some checking link utf8 bytes check).

Suggestion: Allow ignoring return value of target method

I'm using this crate a lot together with the getset crate. It generates getters and setters, and I use this crate to delegate many getters/setters to members.
But the problem is that the generated setters return &mut Self, for chaining, which I don't need/want, and it makes the signatures that I have to write at every level of delegation very long. (I have several use cases where I have multi-level delegation, multiple layers of composition.)
Please allow ignoring the return type of the delegated function and just using () in that case.
E.g. now I have to write this:

delegate! {
    target self.switcher {
        pub fn set_col(&mut self, col: Col) -> &mut Switcher<AppPage, ConnectedDevice>;

        pub fn set_selected_brightness(&mut self, b: f32) -> &mut Switcher<AppPage, ConnectedDevice>;
    }
}

I'd prefer to ignore the return value and just write this:

delegate! {
    target self.switcher {
        pub fn set_col(&mut self, col: Col);

        pub fn set_selected_brightness(&mut self, b: f32);
    }
}

If the parser needs a hint, to know when to insert the extra semicolon,
the syntax could be ;; (double semicolon at the end), it's also memorable because the return value of the target method gets ignored by inserting an extra semicolon.

The big issue with these setters that return &mut Self is that even though I want to allow client-code to set values on my struct's member, I don't want to leak a &mut to my member, because then the client-code could mutate/replace it completely!
This problem would also be solved by being able to ignore the return value when delegating.

Error: Unexpected token `target` in `parse_target`

The following minimal example:

#[macro_use]
extern crate delegate;
extern crate petgraph;
use petgraph::{Graph, Directed, graph::DefaultIx};

pub struct DirectedAcyclicGraph<N, E>(Graph<N, E, Directed, DefaultIx>);

impl<N, E> DirectedAcyclicGraph<N, E> {
    delegate! {
        target self.0 {
            /// Return the total number of nodes in the graph.
            pub fn node_count(&self) -> usize;
        }
    }
}

Fails to compile with the error:

error: no rules expected the token `target`
  --> src/main.rs:10:9
   |
10 |         target self.0 {
   |         ^^^^^^

error: ParseError! Unexpected token `target` in `parse_target`. This is possibly a bug in the delegate library itself. Please file an issue with the following debug information:

{ items : [  ] , action : delegate__expand }


  --> src/main.rs:9:5
   |
9  | /     delegate! {
10 | |         target self.0 {
11 | |             /// Return the total number of nodes in the graph.
12 | |             pub fn node_count(&self) -> usize;
13 | |         }
14 | |     }
   | |_____^
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

This is rustc 1.26.0 on stable-x86_64-apple-darwin.

Better approach for Edition 2018 macro imports

With Rust 2018, the recommended approach is now to use macros instead of extern crate.

For delegate, this now looks something like:

use delegate::{delegate, delegate__parse, delegate__ensure_self, delegate__expand};

If possible, group all these macros under a single import.

Just unwrap

I see an option to unwrap as part of try_into: try_into(unwrap)

But I'd like to just append an .unwrap() call directly - is this possible?

I'm delegating to a method that returns a Result whose only error case is guaranteed not to happen in the delegating struct, so I'd like to unwrap.

New Release Please

Hi,

I've been following the progress on this crate closely, as I needed features that weren't available yet. However, I can now see that master has all the features I need to use this crate fully (namely arbitrary expressions as "targets" for the delegation).
Therefore, can I ask you to release a new version of the crate to crates.io, so that I can depend on that instead of having to depend directly on the repo?

Thanks for all your work, this is a great crate.

transform the return value of `Result` and `Option` type

I've read #48 where attributes like #[method(clone)] was discussed.

I have a specific use case, where I have a wrapper type and want to delegate a lot of method to the inner field. but I'd like to convert the return Result to my error type, something like:

struct Wrapper(Foo);
impl Wrapper {
    // map_err() can be used
    pub fn bar(&self) -> Result<i32, MyError> {
        self.0.bar().map_err(From::from)
    }
    // can also use the question mark operator
    pub fn bar(&self) -> Result<i32, MyError> {
        Ok(self.0.bar()?)
    }
    // sometimes the inner type return Option, convert to Result
    pub fn bar(&self) -> Result<i32, MyError> {
        self.0.bar().ok_or(MyError::EmptyResponse)
    }
    // I didn't have this, but for completeness, convert Result to Option
    pub fn bar(&self) -> Option<i32> {
        self.0.bar().ok()
    }
}

it would be nice if delegate can support this. especially for the question mark case

Nested proc attributes

Is darling currently capable of somehow parsing the following?

#[derive(Debug)]
pub struct SomeName {
    #[attr1(
        attr2("hello"),
        attr2("world")
    )]
    some_arg: String,
}

As far as I could tell using Vec doesn't work right now. And I know of at least one crate who does implement vector-likes using the above syntax (but without the convenience of darling).

The goal is to somehow b able to parse multiple values to a single argument, preferable as Vec<String>.

Add attribute to the return value of all methods

In one of my delegations, only and only .into should be applied to the result of the wrapped value so ended up with this:

delegate! {
    to self.0 {
        #[into]
        fn a<'a>(&mut self, x: T) -> R;

        #[into]
        fn b<'a>(&mut self, x: T) -> R;

        #[into]
        fn c<'a>(&mut self, x: T) -> R;

        // ...
    }
}

It would be great if we could apply the into attribute to all of them at once like:

delegate! {
    #[into]
    to self.0 {
        fn a<'a>(&mut self, x: T) -> R;
        fn b<'a>(&mut self, x: T) -> R;
        fn c<'a>(&mut self, x: T) -> R;
        // ...
    }
}

Bridging sync to async gap with delegate

Hi,
Let's have a struct with a bunch of methods:

struct UserRepo { /* ... */ }

impl UserRepo {
  fn add() { /* ... */ }
  fn del() { /* ... */ }
}

Now we'd like to use this repository in asynchronous code where many workers need to access a single instance of the repository. To guard against race conditions we need to wrap the struct in a Mutex:

struct SharedUserRepo(tokio::sync::Mutex<UserRepo>)

Next step is to delegate add and del calls to a locked instance of UserRepo:

impl SharedUserRepo {
  delegate! {
    to self.0.lock().await {
     async fn add() { /* ... */ }
     async fn del() { /* ... */ }
    }
  }
}

But this code is rejected by rust, so the question is if this can be solved with current delegate or if some extension is needed (like to_async instead of to).

The delegated macro expands to this code, where the second await seems to be invalid:

#[inline(always)]
async fn add(&mut self) {
  self.0.lock().await.add().await;
}

Compatability with async_trait

When using delegate! under trait attributed by #[async_trait], the expand order matters and it seems cannot compile.
IMO, only when delegate! expands before #[async_trait], can this compile and work correctly.

e.g. this won't compile

#[async_trait]
impl Foo for Bar {
    delegate! {
        to self.inner {
            async fn baz(&self);
        }
    }
}

Is there a way to solve this problem?

Delegation to members of members?

Delegation to members of members doesn't seem to work, and the error message is wrong:

image

Can you please add delegation to (members of)+ members? :)
(Any depth should work)

self as target doesn't seem to work

I have a derived getter, using the getset crate, but I also need to impl a trait that has the same getter.
This works:

#[derive(Getters)]
pub struct Struct {
	member: Member,
	#[getset(get)]
	foo: Foo,
}

impl Trait for Struct {
	delegate! {
		to self.member {
			fn bar(&self) -> Bar;
		}
	}

	fn foo(&self) -> &Foo {
		self.foo()
	}
}

But this doesn't work:

impl Trait for Struct {
	delegate! {
		to self.member {
			fn bar(&self) -> Bar;
		}

		to self {
			fn foo(&self) -> &Foo;
		//  ^^ error: expected identifier or integer
		}
	}
}

But it should also work, because the handwritten delegation from trait to self.foo() works :)

Add support support for qualified calls

Consider that the target implements two traits that have a method with the same name. In this case, barely calling the function over the target is ambiguous for the compiler. For example:

trait A {
    fn f(&self);
}

trait B {
    fn f(&self);
}

struct Foo;

impl A for Foo { /* ... */ }
impl B for Foo { /* ... */ }

struct Bar(Foo);

impl A for Bar {
    delegate! {
        to &self.0 {
            fn f(&self);  // Error: multiple applicable items in scope
        }
    }
}

This can be fixed by generating a body that calls the function in a qualified way, i.e., A::f(&self.0);. However, I guess the trait name is not available to the macro so it cannot do that by default.

A solution can be an attribute that takes the trait's name.

delegate! {
    to &self.0 {
        #[through(A)]
        fn f(&self);
    }
}

Is delegating to enum in the target range

Proposed usage is like below:

pub enum WeakRenderObject {
    RenderBox(WeakRenderBox),
    RenderSliver(WeakRenderSliver),
    RenderView(WeakRenderView),
}


impl WeakRenderObject {
    delegate::delegate!(to match self {
        WeakRenderObject::RenderBox(o) => o,
        WeakRenderObject::RenderSliver(o) => o,
        WeakRenderObject::RenderView(o) => o,
    } {
        pub fn is_alive(&self) -> bool;
    });
}

It is expanded as

impl WeakRenderObject {
    #[inline(always)]
    pub fn is_alive(&self) -> bool {
        match self {
            WeakRenderObject::RenderBox(o) => o.is_alive(),
            WeakRenderObject::RenderSliver(o) => o.is_alive(),
            WeakRenderObject::RenderView(o) => o.is_alive(),
        }
    }
}

Allow delegate use in Trait Implementations

I have some code (here) where I would like to re-use code from an external crate, but specifically re-use the trait implementation. I'm not sure if delegate! currently supports this, as it works fine when in the usual impl Struct body, but errors when in impl Trait for Struct body, citing the error E0599 or

no method named x found for typeStruct in current scope
note: the method x exists but the following trait bounds were not satisfied: Struct: Trait

This makes me think that the trait is not in scope of the macro.

For reference the code I am talking about is here:
https://github.com/ethdbg/edb/blob/e5a50342ebcf36b261f98a7e9874a706c0ec3178/emul/src/debug_externalities.rs#L152

(commented out is the code that works)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.