Unsure how to debug this issue with a trivial macro for a book exercise

I'm going through a book called The Complete Rust Reference Guide, and in the topic on Macros, it presents this exercise:

Write a macro that takes an arbitrary number of elements and outputs an unordered HTML list in a literal string, for instance, html_list!([1, 2]) => <ul><li>1/<li><li>2</li></ul> .

My attempt to solve it was as so:

macro_rules! make_list {
    ($($v:tt),*) => {
        {
            let mut str_vec = vec!["<ul>"];
            $(
                str_vec.push(format!("<li>{}</li>", $v));
            )*
            str_vec.push("</ul>");
            str_vec.join("")
        }
    }
}

I attempt to test it with the following test:

        let onetwothree = make_list!(1, 2, 3);
        let abc = make_list!("a", "b", "c");
        assert_eq!("<ul><li>1</li><li>2</li><li>3</li></ul>", onetwothree);
        assert_eq!("<ul><li>a</li><li>b</li><li>c</li></ul>", abc);

I receive the following errors when running the test:

error[E0308]: mismatched types
  --> src/lib.rs:30:27
   |
30 |         let onetwothree = make_list!(1, 2, 3);
   |                           ^^^^^^^^^^^^^^^^^^^ expected `&str`, found struct `std::string::String`
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
  --> src/lib.rs:30:27
   |
30 |         let onetwothree = make_list!(1, 2, 3);
   |                           ^^^^^^^^^^^^^^^^^^^ expected `&str`, found struct `std::string::String`
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> src/lib.rs:31:19
   |
31 |         let abc = make_list!("a", "b", "c");
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found struct `std::string::String`
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> src/lib.rs:31:19
   |
31 |         let abc = make_list!("a", "b", "c");
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found struct `std::string::String`
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> src/lib.rs:31:19
   |
31 |         let abc = make_list!("a", "b", "c");
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found struct `std::string::String`
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0308`.
error: could not compile `mac_exer`.

I tried adding some to_owned() and to_string() and have never gotten the error message to change.
Am I missing something obvious? Also, is there an easier way to debug this?
I realize I am passing the arguments as comma separated values, rather than as a vec, but I didn't think that would matter.

I got it working.

Playground

The issue is that in your macro you created str_vec, which has the type Vec<&str>. Then you tried to push a String into it. I turned everything into a String to quickly get around the issue of freeing borrowed values.

1 Like

I haven’t looked at the book, but I suspect the authors expected you to use the concat! macro:

macro_rules! make_list {
    ($($v:literal),*) => {
        concat!("<ul>",
                $( "<li>", $v, "</li>", )*
                "</ul>")
    }
}
3 Likes

That did it @bob, thank you.
@2e71828's solution complained about the commas inside of the $("<li>", $v, "</li>"), but thanks for letting me know about concat!, I should look more into the built-in macros.

You must have made a typographical error when copying mine;it works fine on the playground. Did you get the trailing comma of the repetition in the right place? (It’s missing entirely in your comment)

I must've made a mistake, you're right it works fine. Thanks again!

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.