Is it possible to write a macro that generates a function where the number of arguments to this function to be a determined by the macro? For instance, I'd like to write something to make using prepared statements in the Cassandra driver easier.
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);
prepare! would need to generate something like (unwrap only here for brevity):
fn some_func(arg1, arg2, arg3) -> Statement {
let mut statement = Statement::new("insert into blah (id, name, reading ) values (?, ?, ?)", 3);
statement.bind_int(0, arg1).unwrap()
.bind_string(1, arg2).unwrap()
.bind_float(2, arg3).unwrap()
}
Two hard things in Rust macros: counting and unique identifers. You have both. Then again, I'm the one writing the answer, so I suppose it's my problem now. At least you didn't ask about parsing the string (which is outright impossible without compiler plugins).
Another impossible thing is mapping types to different methods. You just can't. Instead, I'm going to assume the existence of a helper trait that does this mapping.
Also, Rust doesn't have int, string, or float. I assume you mean i32, String, and f32.
Finally, the way you've written the invocation and expansion don't really gel. I don't see why session is involved; it's not used in the expansion. So I'm going to take the liberty of just pretending you don't need it; if you do, you'll have to hack it back in.
So, with that, here's what I came up with.
// Some dummy types so the following will type-check.
struct Statement;
impl Statement {
fn new(stmt: &str, args: usize) -> Self { Statement }
fn bind_int(self, pos: usize, value: i32) -> Result<Self, ()> { Ok(self) }
fn bind_float(self, pos: usize, value: f32) -> Result<Self, ()> { Ok(self) }
fn bind_string(self, pos: usize, value: String) -> Result<Self, ()> { Ok(self) }
}
struct Session;
impl Session {
fn execute(&self, stmt: Statement) {}
}
// The supporting `BindArgument` trait.
trait BindArgument {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement;
}
impl BindArgument for i32 {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_int(pos, value).unwrap()
}
}
impl BindArgument for f32 {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_float(pos, value).unwrap()
}
}
impl BindArgument for String {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_string(pos, value).unwrap()
}
}
// The macro itself.
macro_rules! prepare {
// These three are taken straight from
// 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};
// This is how we bind *one* argument.
(#bind_arg $stmt:expr, $args:expr, $pos:tt, $t:ty) => {
prepare!(#as_expr <$t as BindArgument>::bind($stmt, $pos, $args.$pos))
};
// This is how we bind *N* arguments. Note that because you can't do
// arithmetic in macros, we have to spell out every supported integer.
// This could *maybe* be factored down with some more work, but that
// can be homework. ;)
(#bind_args $stmt:expr, $args:expr, 0, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 0, $next), $args, 1, $($tys,)*)
};
(#bind_args $stmt:expr, $args:expr, 1, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 1, $next), $args, 2, $($tys,)*)
};
(#bind_args $stmt:expr, $args:expr, 2, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 2, $next), $args, 3, $($tys,)*)
};
(#bind_args $stmt:expr, $_args:expr, $_pos:tt,) => {
$stmt
};
// Finally, the entry point of the macro.
($stmt:expr, $($tys:ty),* $(,)*) => {
{
// I cheated: rather than face the horror of trying to *also* do
// unique identifiers, I just shoved the arguments into a tuple, so
// that I could just re-use the position.
fn prepared_statement(args: ($($tys,)*)) -> Statement {
let statement = Statement::new(
$stmt,
prepare!(#count_tts $(($tys))*));
prepare!(#bind_args statement, args, 0, $($tys,)*)
}
prepared_statement
}
};
}
fn main() {
let session = Session;
let prepared = prepare!(
r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
i32, String, f32);
// Don't use .to_string() for &str -> String; it's horribly inefficient.
let stmt = prepared((1, "test".to_owned(), 3.1));
session.execute(stmt);
}
And here's what the main function expands to, to give you a frame of reference:
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);
}
Related
During program execution, the value of spp_pipe_count_setup is set. I need to transfer this value to the database.
I'm having a problem with Rust closures:
cannot assign to data_pipe_count.value_int, as it is a captured variable in a Fn closure
struct SetupDataSQL<'a> {
value_int: i32,
}
fn main() {
let spp_pipe_count_setup = gtk::SpinButton::with_range(1.0, 10.0, 1.0);
let mut data_pipe_count = SetupDataSQL {
value_int: 0
};
spp_pipe_count_setup.connect_value_changed(move |_| {
data_pipe_count.value_int = spp_pipe_count_setup.value() as i32
});
}
And after that i am passing data_pipe_count.value_int to sql database.
client.execute(
"INSERT INTO setup_data (param_Name, value_int) VALUES ($1, $2)",
&[&data_pipe_count.param_name, &data_pipe_count.value_int],
.unwrap();
I get a different error if I use RefCell in struct: the trait bound RefCell<i32>: ToSql is not satisfied
struct SetupDataSQL<'a> {
param_name: &'a str,
value_int: RefCell<i32>
}
client.execute(
"INSERT INTO setup_data (param_Name, value_int, value_float, value_char) VALUES ($1, $2)",
&[&data_pipe_count.param_name, &data_pipe_count.borrow().value_int],
).unwrap();
No matter how hard I try to pass the value of the SpinButton to the DB, it doesn't work.
Error: the trait bound Rc<RefCell<{integer}>>: ToSql is not satisfied
let temp = Rc::new(RefCell::new(1));
let temp = temp clone();
let mut client = (Client::connect("postgresql://user:pass#localhost:5432/postgres", NoTls).unwrap());
client.execute(
"INSERT INTO setup_data ( value_int) VALUES ($1)",
&[ &(*temp.borrow_mut()]),
.unwrap();
In each of the examples below, does blob.0 or text need to be copied? How do you know?
Setup
import SQLite3
private let static_destructor = unsafeBitCast(0, to: sqlite3_destructor_type.self)
private let transient_destructor = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
Examples
bind_blob
func bind_blob(_ stmt: OpaquePointer, _ blob: (UnsafeRawPointer, Int32)) {
sqlite3_bind_blob(stmt, 1, blob.0, blob.1, transient_destructor)
}
bind_blobs
func bind_blobs(_ stmt: OpaquePointer, _ blobs: [(UnsafeRawPointer, Int32)]) {
for (index, blob) in blobs.enumerated() {
sqlite3_bind_blob(stmt, Int32(index+1), blob.0, blob.1, transient_destructor)
}
}
bind_text
func bind_text(_ stmt: OpaquePointer, _ text: String) {
sqlite3_bind_text(stmt, 1, text, -1, transient_destructor)
}
bind_texts
func bind_texts(_ stmt: OpaquePointer, _ texts: [String]) {
for (index, text) in texts.enumerated() {
sqlite3_bind_text(stmt, Int32(index+1), text, -1, transient_destructor)
}
}
Ie, should I use static_destructor instead of transient_destructor in any of the examples?
Related question: When to use SQLITE_TRANSIENT vs SQLITE_STATIC?
In your bind_text and bind_texts functions, you need to use the transient destructor. When you pass a Swift String to a C function as a const char * argument, Swift does not guarantee that the pointer will remain valid after the C function returns. The Calling Functions With Pointer Parameters article says this:
The pointer you pass to the function is only guaranteed to be valid for the duration of the function call. Do not persist the pointer and access it after the function has returned.
In your bind_blob and bind_blobs functions, it depends where the UnsafeRawPointer comes from and when you execute the SQL statement. If you're getting the pointer using any sort of Swift withUnsafeWhatever function, then the pointer is not valid after the withUnsafeWhatever function returns. For example, the withUnsafeBytes(of:_:) documentation says this:
The buffer pointer argument is valid only for the duration of the closure’s execution.
If you bind your blob, execute the statement, and then no longer use the binding, then you can use the static destructor. This is okay with the static destructor:
let data: Data = ...
data.withUnsafeBytes { rawBuffer in
if let pointer = rawBuffer.baseAddress {
bind_blob(statement, (pointer, rawBuffer.count))
execute(statement)
// No more use of statement unless the parameter is rebound.
}
}
But this is not okay with the static destructor:
let data: Data = ...
data.withUnsafeBytes { rawBuffer in
if let pointer = rawBuffer.baseAddress {
bind_blob(statement, (pointer, rawBuffer.count))
}
}
execute(statement)
I want to create a macro that prints "Hello" a specified number of times. It's used like:
many_greetings!(3); // expands to three `println!("Hello");` statements
The naive way to create that macro is:
macro_rules! many_greetings {
($times:expr) => {{
println!("Hello");
many_greetings!($times - 1);
}};
(0) => ();
}
However, this doesn't work because the compiler does not evaluate expressions; $times - 1 isn't calculated, but fed as a new expression into the macro.
While the ordinary macro system does not enable you to repeat the macro expansion many times, there is no problem with using a for loop in the macro:
macro_rules! many_greetings {
($times:expr) => {{
for _ in 0..$times {
println!("Hello");
}
}};
}
If you really need to repeat the macro, you have to look into procedural macros/compiler plugins (which as of 1.4 are unstable, and a bit harder to write).
Edit: There are probably better ways of implementing this, but I've spent long enough on this for today, so here goes. repeat!, a macro that actually duplicates a block of code a number of times:
main.rs
#![feature(plugin)]
#![plugin(repeat)]
fn main() {
let mut n = 0;
repeat!{ 4 {
println!("hello {}", n);
n += 1;
}};
}
lib.rs
#![feature(plugin_registrar, rustc_private)]
extern crate syntax;
extern crate rustc;
use syntax::codemap::Span;
use syntax::ast::TokenTree;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use rustc::plugin::Registry;
use syntax::util::small_vector::SmallVector;
use syntax::ast::Lit_;
use std::error::Error;
fn expand_repeat(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
let mut parser = cx.new_parser_from_tts(tts);
let times = match parser.parse_lit() {
Ok(lit) => match lit.node {
Lit_::LitInt(n, _) => n,
_ => {
cx.span_err(lit.span, "Expected literal integer");
return DummyResult::any(sp);
}
},
Err(e) => {
cx.span_err(sp, e.description());
return DummyResult::any(sp);
}
};
let res = parser.parse_block();
match res {
Ok(block) => {
let mut stmts = SmallVector::many(block.stmts.clone());
for _ in 1..times {
let rep_stmts = SmallVector::many(block.stmts.clone());
stmts.push_all(rep_stmts);
}
MacEager::stmts(stmts)
}
Err(e) => {
cx.span_err(sp, e.description());
DummyResult::any(sp)
}
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("repeat", expand_repeat);
}
added to Cargo.toml
[lib]
name = "repeat"
plugin = true
Note that if we really don't want to do looping, but expanding at compile-time, we have to do things like requiring literal numbers. After all, we are not able to evaluate variables and function calls that reference other parts of the program at compile time.
As the other answers already said: no, you can't count like this with declarative macros (macro_rules!).
But you can implement the many_greetings! example as a procedural macro. procedural macros were stabilized a while ago, so the definition works on stable. However, we can't yet expand macros into statements on stable -- that's what the #![feature(proc_macro_hygiene)] is for.
This looks like a lot of code, but most code is just error handling, so it's not that complicated!
examples/main.rs
#![feature(proc_macro_hygiene)]
use count_proc_macro::many_greetings;
fn main() {
many_greetings!(3);
}
Cargo.toml
[package]
name = "count-proc-macro"
version = "0.1.0"
authors = ["me"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "0.6"
src/lib.rs
extern crate proc_macro;
use std::iter;
use proc_macro::{Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned};
/// Expands into multiple `println!("Hello");` statements. E.g.
/// `many_greetings!(3);` will expand into three `println`s.
#[proc_macro]
pub fn many_greetings(input: TokenStream) -> TokenStream {
let tokens = input.into_iter().collect::<Vec<_>>();
// Make sure at least one token is provided.
if tokens.is_empty() {
return err(Span::call_site(), "expected integer, found no input");
}
// Make sure we don't have too many tokens.
if tokens.len() > 1 {
return err(tokens[1].span(), "unexpected second token");
}
// Get the number from our token.
let count = match &tokens[0] {
TokenTree::Literal(lit) => {
// Unfortunately, `Literal` doesn't have nice methods right now, so
// the easiest way for us to get an integer out of it is to convert
// it into string and parse it again.
if let Ok(count) = lit.to_string().parse::<usize>() {
count
} else {
let msg = format!("expected unsigned integer, found `{}`", lit);
return err(lit.span(), msg);
}
}
other => {
let msg = format!("expected integer literal, found `{}`", other);
return err(other.span(), msg);
}
};
// Return multiple `println` statements.
iter::repeat(quote! { println!("Hello"); })
.map(TokenStream::from)
.take(count)
.collect()
}
/// Report an error with the given `span` and message.
fn err(span: Span, msg: impl Into<String>) -> TokenStream {
let msg = msg.into();
quote_spanned!(span.into()=> {
compile_error!(#msg);
}).into()
}
Running cargo run --example main prints three "Hello"s.
For those looking for a way to do this, there is also the seq_macro crate.
It is fairly easy to use and works out of the box with stable Rust.
use seq_macro::seq;
macro_rules! many_greetings {
($times:literal) => {
seq!{ N in 0..$times {
println!("Hello");
}}
};
}
fn main() {
many_greetings!(3);
many_greetings!(12);
}
As far as I know, no. The macro language is based on pattern matching and variable substitution, and only evaluates macros.
Now, you can implement counting with evaluation: it just is boring... see the playpen
macro_rules! many_greetings {
(3) => {{
println!("Hello");
many_greetings!(2);
}};
(2) => {{
println!("Hello");
many_greetings!(1);
}};
(1) => {{
println!("Hello");
many_greetings!(0);
}};
(0) => ();
}
Based on this, I am pretty sure one could invent a set of macro to "count" and invoke various operations at each step (with the count).
I need to get index of macro repetition element to write next code:
struct A {
data: [i32; 3]
}
macro_rules! tst {
( $( $n:ident ),* ) => {
impl A {
$(
fn $n(self) -> i32 {
self.data[?] // here I need the index
}
),*
}
}
}
I know one way to do it: just tell user to write index by hands:
( $( $i:ident => $n:ident ),* )
But is there a more elegant way which does not require user's action?
The easiest way is to use recursion, like so:
struct A {
data: [i32; 3]
}
macro_rules! tst {
(#step $_idx:expr,) => {};
(#step $idx:expr, $head:ident, $($tail:ident,)*) => {
impl A {
fn $head(&self) -> i32 {
self.data[$idx]
}
}
tst!(#step $idx + 1usize, $($tail,)*);
};
($($n:ident),*) => {
tst!(#step 0usize, $($n,)*);
}
}
tst!(one, two, three);
fn main() {
let a = A { data: [10, 20, 30] };
println!("{:?}", (a.one(), a.two(), a.three()));
}
Note that I changed the method to take &self instead of self, since it made writing the example in the main function easier. :)
Each step in the recursion just adds 1 to the index. It is a good idea to use "typed" integer literals to avoid compilation slowdown due to lots and lots of integer inference.
I'd like to create a setter/getter pair of functions where the names are automatically generated based on a shared component, but I couldn't find any example of macro rules generating a new name.
Is there a way to generate code like fn get_$iden() and SomeEnum::XX_GET_$enum_iden?
If you are using Rust >= 1.31.0 I would recommend using my paste crate which provides a stable way to create concatenated identifiers in a macro.
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define the struct. This expands to:
//
// pub struct S {
// a: String,
// b: String,
// c: String,
// }
pub struct $name {
$(
$field: String,
)*
}
paste::item! {
// An impl block with getters. Stuff in [<...>] is concatenated
// together as one identifier. This expands to:
//
// impl S {
// pub fn get_a(&self) -> &str { &self.a }
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
impl $name {
$(
pub fn [<get_ $field>](&self) -> &str {
&self.$field
}
)*
}
}
};
}
make_a_struct_and_getters!(S { a, b, c });
My mashup crate provides a stable way to create new identifiers that works with any Rust version >= 1.15.0.
#[macro_use]
extern crate mashup;
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define the struct. This expands to:
//
// pub struct S {
// a: String,
// b: String,
// c: String,
// }
pub struct $name {
$(
$field: String,
)*
}
// Use mashup to define a substitution macro `m!` that replaces every
// occurrence of the tokens `"get" $field` in its input with the
// concatenated identifier `get_ $field`.
mashup! {
$(
m["get" $field] = get_ $field;
)*
}
// Invoke the substitution macro to build an impl block with getters.
// This expands to:
//
// impl S {
// pub fn get_a(&self) -> &str { &self.a }
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
m! {
impl $name {
$(
pub fn "get" $field(&self) -> &str {
&self.$field
}
)*
}
}
}
}
make_a_struct_and_getters!(S { a, b, c });
No, not as of Rust 1.22.
If you can use nightly builds...
Yes: concat_idents!(get_, $iden) and such will allow you to create a new identifier.
But no: the parser doesn't allow macro calls everywhere, so many of the places you might have sought to do this won't work. In such cases, you are sadly on your own. fn concat_idents!(get_, $iden)(…) { … }, for example, won't work.
There's a little known crate gensym that can generate unique UUID names and pass them as the first argument to a macro, followed by a comma:
macro_rules! gen_fn {
($a:ty, $b:ty) => {
gensym::gensym!{ _gen_fn!{ $a, $b } }
};
}
macro_rules! _gen_fn {
($gensym:ident, $a:ty, $b:ty) => {
fn $gensym(a: $a, b: $b) {
unimplemented!()
}
};
}
mod test {
gen_fn!{ u64, u64 }
gen_fn!{ u64, u64 }
}
If all you need is a unique name, and you don't care what it is, that can be useful. I used it to solve a problem where each invocation of a macro needed to create a unique static to hold a singleton struct. I couldn't use paste, since I didn't have unique identifiers I could paste together in the first place.