Macros 宏,该宏生成具有由宏确定的参数的函数

Macros 宏,该宏生成具有由宏确定的参数的函数,macros,rust,Macros,Rust,是否可以编写一个宏来生成一个函数,该函数的参数数量由宏决定?例如,我想写一些东西,使在Cassandra驱动程序中使用准备好的语句变得更容易 let prepared = prepare!(session, "insert into blah (id, name, reading ) values (?, ?, ?)", int, string, float); let stmt = prepared(1, "test".to_string(), 3.1); session.execute(st

是否可以编写一个宏来生成一个函数,该函数的参数数量由宏决定?例如,我想写一些东西,使在Cassandra驱动程序中使用准备好的语句变得更容易

let prepared = prepare!(session, "insert into blah (id, name, reading ) values (?, ?, ?)", int, string, float);
let stmt = prepared(1, "test".to_string(), 3.1);
session.execute(stmt);
准备将需要生成以下内容(为简洁起见,仅在此处展开):


Rust宏中有两件困难的事情:计数和唯一标识符。你两者都有。再说一遍,是我写的答案,所以我想这是我的问题了。至少您没有询问解析字符串的问题(如果没有编译器插件,这是完全不可能的)

另一件不可能的事情是将类型映射到不同的方法。你就是不能。相反,我将假设存在一个帮助者特征来完成这个映射

此外,Rust没有
int
string
float
。我想你是指
i32
String
f32

最后,您编写调用和扩展的方式并没有真正起作用。我不明白为什么会涉及到
会话
;扩展中没有使用它。所以我要冒昧地假装你不需要它;如果你这么做了,你就得把它重新破解

所以,我想到了这个

//一些伪类型,因此下面将进行类型检查。
结构语句;
impl语句{
fn new(stmt:&str,args:usize)->Self{Statement}
fn bind_int(self,pos:usize,value:i32)->结果{Ok(self)}
fn bind_float(self,pos:usize,value:f32)->结果{Ok(self)}
fn bind_string(self,pos:usize,value:string)->结果{Ok(self)}
}
结构会话;
impl会话{
fn执行(&self,stmt:Statement){}
}
//支持“BindArgument”的特性。
特征绑定参数{
fn绑定(stmt:Statement,pos:usize,value:Self)->语句;
}
i32的impl bind参数{
fn绑定(stmt:Statement,pos:usize,value:Self)->语句{
stmt.bind_int(位置,值).unwrap()
}
}
f32的impl bind参数{
fn绑定(stmt:Statement,pos:usize,value:Self)->语句{
stmt.bind_float(位置,值).unwrap()
}
}
字符串的impl bind参数{
fn绑定(stmt:Statement,pos:usize,value:Self)->语句{
stmt.bind_字符串(位置,值).unwrap()
}
}
//宏本身。
宏规则!预备{
//这三个是直接从
// https://danielkeep.github.io/tlborm/book/
(@as_expr$e:expr)=>{$e};
(@count_tts$($tts:tt)*)=>{
::len(&[$(prepare!(@replace_tt$tts()),*)
};
(@replace_tt$_tt:tt$e:expr)=>{$e};
//这就是我们绑定*一个*参数的方式。
(@bind_arg$stmt:expr,$args:expr,$pos:tt,$t:ty)=>{
准备!(@as_expr::bind($stmt,$pos,$args.$pos))
};
//这就是我们绑定*N*个参数的方式。请注意,因为您不能这样做
//算术在宏中,我们必须拼出每个支持的整数。
//这可以通过更多的工作来考虑,但是
//可以是家庭作业
(@bind_args$stmt:expr,$args:expr,0,$next:ty,$($tys:ty,)*)=>{
准备!(@bind_args prepare!(@bind_arg$stmt,$args,0,$next),$args,1,$($tys,)*)
};
(@bind_args$stmt:expr,$args:expr,1,$next:ty,$($tys:ty,)*)=>{
准备!(@bind_args prepare!(@bind_arg$stmt,$args,1,$next),$args,2,$($tys,)*)
};
(@bind_args$stmt:expr,$args:expr,2,$next:ty,$($tys:ty,)*)=>{
准备!(@bind_args prepare!(@bind_arg$stmt,$args,2,$next),$args,3,$($tys,)*)
};
(@bind_args$stmt:expr,$_args:expr,$_pos:tt,)=>{
$stmt
};
//最后是宏的入口点。
($stmt:expr,$($tys:ty),*$(,)*)=>{
{
//我欺骗了自己:而不是面对尝试去做的恐惧
//唯一标识符,我只是把参数放入一个元组,所以
//我可以重新利用这个职位。
fn编制的_报表(args:($($tys,)*)->报表{
let语句=语句::新建(
$stmt,
准备!(@count_tts$($tys))*);
准备!(@bind_args语句,args,0,$($tys,)*)
}
准备好的声明
}
};
}
fn main(){
让会话=会话;
让我们准备好(
r#“插入blah(id、名称、读数)值(?,?)”#,
i32,字符串,f32);
//不要将.to_string()用于&str->string;这样效率极低。
设stmt=prepared((1,“test.”to_owned(),3.1));
session.execute(stmt);
}
以下是
main
函数的扩展内容,为您提供一个参考框架:

fn main() {
    let session = Session;
    let prepared = {
        fn prepared_statement(args: (i32, String, f32)) -> Statement {
            let statement = Statement::new(
                r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
                <[()]>::len(&[(), (), ()]));
            <f32 as BindArgument>::bind(
                <String as BindArgument>::bind(
                    <i32 as BindArgument>::bind(
                        statement, 0, args.0),
                    1, args.1),
                2, args.2)
        }
        prepared_statement
    };
    // Don't use .to_string() for &str -> String; it's horribly inefficient.
    let stmt = prepared((1, "test".to_owned(), 3.1));
    session.execute(stmt);
}
fn main(){
让会话=会话;
让我们准备好={
fn准备的_语句(args:(i32,String,f32))->语句{
let语句=语句::新建(
r#“插入blah(id、名称、读数)值(?,?)”#,
::len(&[(),(),());
::绑定(
::绑定(
::绑定(
语句,0,args.0),
1,参数1),
2,args.2)
}
准备好的声明
};
//不要将.to_string()用于&str->string;这样效率极低。
设stmt=prepared((1,“test.”to_owned(),3.1));
session.execute(stmt);
}

请删除您的第二个问题,关于“这可能吗?”和。我最初是用原生锈菌类型编写的,但希望int->bind_int可以很容易地侵入,而不必显式支持每种类型。不过,使用本机类型完全可以。谢谢你给出了这个令人难以置信的答案。因为我稍后会解析和验证查询的语法,所以我最好直接使用编译器插件?我已经基本完成了解析器,所以我将添加它
fn main() {
    let session = Session;
    let prepared = {
        fn prepared_statement(args: (i32, String, f32)) -> Statement {
            let statement = Statement::new(
                r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
                <[()]>::len(&[(), (), ()]));
            <f32 as BindArgument>::bind(
                <String as BindArgument>::bind(
                    <i32 as BindArgument>::bind(
                        statement, 0, args.0),
                    1, args.1),
                2, args.2)
        }
        prepared_statement
    };
    // Don't use .to_string() for &str -> String; it's horribly inefficient.
    let stmt = prepared((1, "test".to_owned(), 3.1));
    session.execute(stmt);
}