-2

This is my dummy code that doesn't actually work, I would like to know how, or is there a way to achieve this functionality.

Currently if I want to do this, I need to define an enum and match, which is inefficient and difficult to maintain.

pub fn mainrun() {
    let aimpl = MyStruct {
        Name: "bar".to_string(),
    };
    // dummy method collector
    let some_dummy = impl_method_collector(&aimpl);
    for k in some_dummy {
        k();
    }
    /*expected to get 
    bar say one
    bar say two
    bar say three
    */
}

struct MyStruct {
    Name: String,
}

impl MyStruct {
    fn one_fn(&self) {
        println!("{:?} say one", self.Name)
    }
    fn two_fn(&self) {
        println!("{:?} say two", self.Name)
    }
    fn three_fn(&self) {
        println!("{:?} say three", self.Name)
    }
}

Here is how I achieve the same in Go. I want to achieve something like this with Rust.

package main

import "reflect"

func main() {
    println("start")
    astr := &MyStruct{"bar"}
    v := reflect.ValueOf(astr)
    vNums := v.NumMethod()
    for i := 0; i < vNums; i++ {
        v.Method(i).Call([]reflect.Value{})
    }
    /*expected to get 
        start
        bar say one
        bar say three
        bar say tow
    */
}

type MyStruct struct {
    Name string
}

func (m *MyStruct) FnOne() {
    println(m.Name, "say one")
}

func (m *MyStruct) FnTow() {
    println(m.Name, "say tow")
}

func (m *MyStruct) FnThree() {
    println(m.Name, "say three")
}
E_net4
  • 27,810
  • 13
  • 101
  • 139
AM031447
  • 475
  • 1
  • 8
  • 21
  • 1
    Are you saying you want to call every method belonging to a type? May I ask: *why?* – gspr Jan 14 '22 at 09:14
  • As far as I know Rust doesn't have SFINAE, and without it it's not clear for me how can compiler be sure that every instance method doesn't require some other argument. Without it the program might get ill-formed easily – Alexey S. Larionov Jan 14 '22 at 09:15
  • How do you plan to handle methods with different argument lists and return types? – the busybee Jan 14 '22 at 09:18
  • @gspr Yes, I want to simplify the routing design so that each implementation is automatically registered instead of enumerating the matches, finding them is just the first step – AM031447 Jan 14 '22 at 09:20
  • @thebusybee This is a little complicated. When writing golang, it will use middleware to encode and decode, serialize and deserialize, and the ultimate goal is to automatically register with the routing table, instead of having to modify the enumeration table every time a new function is added, and develop new The function only needs to focus on what structure is received and what structure is returned, which reduces development pressure and is quite smooth – AM031447 Jan 14 '22 at 09:26
  • 1
    https://stackoverflow.com/q/36416773 Rust does not have that form of run-time reflection. It is more idiomatic to make all intervening structs implement a common trait. – E_net4 Jan 14 '22 at 09:42
  • 2
    On a side note, [struct field names are generally in lower_snake_case](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case). – E_net4 Jan 14 '22 at 09:43
  • @E_net4ismydisplayname It means that it is possible to make the struct return all its implementations through the trait? How can I make a function like this? – AM031447 Jan 14 '22 at 09:48
  • 2
    It means that it is more idiomatic to depend on a trait of your design than on runtime reflection. How this trait is written is up to what exactly you wish to achieve. Should compile time reflection be a possibility, the implementation may be assisted with macros. – E_net4 Jan 14 '22 at 09:59
  • 1
    Does [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d99a43fb09bb538d26edd057adb4dfbb) do what you want? – Jmb Jan 14 '22 at 11:33
  • @Jmb Yes, this is it! Thank you so much, I'm almost desperate! – AM031447 Jan 15 '22 at 06:57

1 Answers1

1

You can do something similar using a macro that defines all the "route" methods along with any "global" methods that want to use the list of "routes":

macro_rules! make_routes {
    ($name:ident $(fn $method:ident (&$self:ident) { $($code:tt)* })*) => {
        impl $name {
            $(fn $method (&$self) { $($code)* })*
            
            // Here you define the method (or methods) that operate on the list
            // of "routes".
            fn call_all (&self) {
                $(self.$method();)*
            }
        }
    }
}

Then you call it like this:

struct MyStruct {
    a: i32,
}

make_routes!{
    MyStruct
    fn route1 (&self) {
        println!("Route 1");
    }
    fn route2 (&self) {
        println!("Route 2 (a = {})", self.a);
    }
}

Playground

Note that if the methods you want to call take extra parameters, then you will need to list the parameter names for each method (but you don't need to list the types since they must be the same anyway):

macro_rules! make_routes {
    ($name:ident $(fn $method:ident (&$self:ident, $param:pat) { $($code:tt)* })*) => {
        impl $name {
            $(fn $method (&$self, $param: i32) { $($code)* })*
            
            fn call_all (&self, param: i32) {
                $(self.$method (param);)*
            }
        }
    }
}

make_routes!{
    MyStruct
    fn route1 (&self, param) {
        println!("Route 1 (param = {})", param);
    }
    fn route2 (&self, param) {
        println!("Route 2 (a = {}, param = {})", self.a, param);
    }
}

Playground

For more details on macros, you can read The Little Book of Rust Macros.

Jmb
  • 18,893
  • 2
  • 28
  • 55