I wonder if w can write a macro to generate this tuple type (String, String, String) to something like a shorter form (String; 3) in Rust?.
Tuples are typically used for a mix of types, but for a repeated type, how about a [String; 3]
array?
I expect it is possible to write a macro like tuple!(String; 3)
, but I'm not sure why you want that.
The reason is I use one method from a library (redis-rs
) and the method requires me to define type in tuple so that I can view the return data from the method, I guess they base on the type to cast the value. In my case, this method is dynamic and sometimes it returns 2 sometimes 3.
if you only need two or three element homogenous tuples, a macro would be as easy as:
macro_rules! repeat {
($t:ty ; 2) => { ($t, $t) };
($t:ty ; 3) => { ($t, $t, $t) };
}
fn default<T: Default>() -> T {
T::default()
}
fn main() {
let t: repeat!(&str; 3) = default();
assert_eq!(t, ("", "", ""));
}
No, 2-3 is only an example. Are there any way to support this to 1..n elements?
What a neat trick, very cool!
Also an idea would be a function-like procedural macro like this:
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, LitInt, Result, Token, Type};
use quote::quote;
struct Args {
ty: Type,
repeat: usize,
}
impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let ty = input.parse()?;
let _: Token![;] = input.parse()?;
let repeat = input.parse::<LitInt>()?.base10_parse()?;
Ok(Args { ty, repeat })
}
}
#[proc_macro]
pub fn repeat(input: TokenStream) -> TokenStream {
let args = parse_macro_input!(input as Args);
let ty = args.ty;
let mut res = quote! {};
for _ in 0..args.repeat {
res = quote! { #res #ty, };
}
quote! { ( #res ) }.into()
}
fn default<T: Default>() -> T {
T::default()
}
#[test]
fn repeat3() {
let t: repeat!(&str; 3) = default();
assert_eq!(t, ("", "", ""));
}
#[test]
fn repeat10() {
let t: repeat!(&str; 10) = default();
assert_eq!(t, ("", "", "", "", "", "", "", "", "", ""));
}
Only works with literals though, something along the lines of:
const N: usize = 10;
let t: repeat(&str; N) = default();
won't work
Are there anyway to pass dynamic N into this repeat
macro? I'm thinking to pass a function into a macro and then invoke it to get the value.
No, macros are a purely compile-time (what's more, syntactic) abstraction. They don't even have access to type information, let alone run-time values. What you are trying to do doesn't make sense anyway – Rust is statically typed, so you can't create different, value-dependent types at runtime. (The closest thing would be an enum or a trait object, but that too would require explicitly listing a finite set of pre-determined types.)
Beware that the codegen/quoting part is accidentally quadratic! A more "proper" solution would be:
let iter = std::iter::repeat(args.ty).take(args.repeat);
quote!{ (#(#iter,)* }
Gotcha! Thanks everyone for helping me with my problem
You are completely right, I tent to forget that quote!
also supports repetition like declarative macros .
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.