Crate for count_tts macro?

I can't find a crate with a simple count_tts macro. Example: count_tts!(a b c) == 3. Is there one out there? Would it be useful if I published one? If yes, please read on and review my code.

I found this post by @tobia which seems to be the best implementation. But I made a couple changes. I removed the usize annotations. Since the expansion is logarithmic, I don't think this will present an issue with type inference slowness. I also reduced recursion for odd cases.

Does this qualify as a one-size-fits-all solution?

lib.rs

#![no_std]

#[macro_export]
macro_rules! count_tts {
    () => (0);
    ($one:tt) => (1);
    ($($a:tt $b:tt)*) => (count_tts!($($a)*) << 1);
    ($first:tt $($a:tt $b:tt)*) => (count_tts!($($a)*) << 1 | 1);
}
1 Like
macro_rules! one { ($t:tt) => (1) }
macro_rules! count_tts {
    ($($t:tt)*) => (0 $(+ one!($t))*)
}

I personally don't think it is useful in a crate. Maybe in a macro snippets collection.
(I copied this from one of my crates and modified it to fit this usecase.)

Note that your version is linear in the number of tts, unlike the ones above, which are logarithmic. For example, when counting a sequence of length 128, your version will expand to 128 operations, while the other versions will only use 7 operations.

For cases where the size of the expansion matters, it does seem useful to publish the efficient version in a crate for re-use.

Went ahead and published! https://crates.io/crates/count_tts

Careful, you forgot to $crate:: qualify your recursive calls to the macro.

This means that a user doing:

fn main ()
{
     let _ = ::count_tts::count_tts!( will fail );
}

would get an error.

  • Tip: when testing a macro that will be exported, use integration tests. Ideally two: one that uses #[macro_use] extern crate ... and another that just uses fully-qualified macro calls like in my example :wink:.

Aside

If we are to use a dedicated crate to export a macro that does this, I personally would prefer if it were done with a procedural macro:

use ::proc_macro::*;

#[proc_macro] pub
fn count_tts (input: TokenStream) -> TokenStream
{
    TokenTree::Literal(
        Literal::usize_unsuffixed(input.into_iter().count())
    ).into()
}

This has the ever diminishing drawback of requiring Rust 1.45.0, but has the advantage of yielding a far more readable cargo expand-ed code, which is not to be underestimated.

3 Likes

Thank you for the tips, @Yandros ! Darn, I was so proud of my macro_rules! :sob: but I have to agree the procedural macro is better. I will update the crate.

It is actually a very neat one you had there :wink:

A good middle-ground, is to add your macro_rules! implementation in the documentation of your crate, something like:

I dunno... I feel like people can be too scared of dependencies.

For the paranoid, a proc-macro dependency deserves more scrutiny because it executes code at build time, within the rustc process.

1 Like

Very well then. I updated the readme.

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.