Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.6k views
in Technique[技术] by (71.8m points)

dynamic - How to dynamically build function calls with different numbers of arguments in Rust?

How do I take a vector of function argument AST variants, extract the values, and use them to instantiate a function call?

I am writing an interpreter that evaluates certain expressions. Some of the expressions are function calls. I am having a hard time figuring out how to translate the function calls AST to the actual call. The AST gives me the function name and a vector of arguments. I can lookup the function pointer to call from the name using a map, but passing the arguments to the function pointer is problem.

Rust does not have a splat operator (argument expansion). I could pass them as a tuple and use destructuring of the arguments, but I can't figure out how to convert the vector of AST argument enum variants to a tuple of the concrete types.

I can't simply map or loop over the AST arguments to extract the values and produce a tuple.

I can use nested tuples to build a heterogenous list incrementally:

fn prepend<I,T>(i: I, t: T) -> (I,T) { (i, t) }

fn foo() {
    let x = ();
    let x = prepend(1, x);
    let x = prepend(2.0, x);
    let x = prepend(true, x);
}

But that only works because x gets shadowed and the new binding has a different type. This won't work:

fn foo() {
    let mut x = ();
    x = prepend(1, x);
    x = prepend(2.0, x);
    x = prepend(true, x);
}

Any ideas?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You don't. Rust is a statically typed language and you are attempting to do non-statically-determinable actions.

Instead, all of your functions need to take in a collection of arguments, verify that there is the right number of arguments (and type, if appropriate to your interpreter), then call the appropriate Rust function with a fixed number of arguments:

// All of the panicking can be replaced by proper error handling.

enum Arg {
    Bool(bool),
    Int(i32),
}

impl Arg {
    fn into_bool(self) -> bool {
        match self {
            Arg::Bool(b) => b,
            _ => panic!("Not a bool"),
        }
    }

    fn into_int(self) -> i32 {
        match self {
            Arg::Int(i) => i,
            _ => panic!("Not an int"),
        }
    }
}

fn some_fn_wrapper(mut args: Vec<Arg>) {
    assert_eq!(args.len(), 3);

    let c = args.pop().unwrap();
    let b = args.pop().unwrap();
    let a = args.pop().unwrap();

    some_fn(a.into_bool(), b.into_int(), c.into_bool())
}

fn some_fn(_a: bool, _b: i32, _c: bool) {}

All of this will happen at runtime, as you want to create a highly dynamic language.

See also:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...