Conflicts importing 'Write' for both Recursive Macros and try! Macros


#1

When the following code is run it gives error:

<anon>:9:5: 9:25 error: a type named `Write` has already been imported in this module [E0251]
<anon>:9 use std::io::prelude::*;

If all the code under each of the “TRY RECURSIVE MACROS” comments (PARTS 1, 2 and 3) is removed, then all the “TRY ERRORS” code compiles successfully.

Conversely, if all the code under each of the “TRY ERRORS” comments (PARTS 1, 2 and 3) is removed, then all the “TRY RECURSIVE MACROS” code compiles successfully.

I want it to compile successfully with both the try_recursive_macros() function and the try_errors() function included.

use std::old_io;

// START - TRY RECURSIVE MACROS PART 1 OF 3
use std::fmt::Write;
// END - TRY RECURSIVE MACROS PART 1 OF 3

// START - TRY ERRORS PART 1 OF 3
use std::fs::File;
use std::io;
use std::io::prelude::*;
// END - TRY ERRORS PART 1 OF 3

fn main() {

    // START - TRY RECURSIVE MACROS PART 2 OF 3
    try_recursive_macros();
    // END - TRY RECURSIVE MACROS PART 2 OF 3

    // START - TRY ERRORS PART 2 OF 3
    try_errors(); 
    // END - TRY ERRORS PART 2 OF 3

}

// START - TRY RECURSIVE MACROS PART 3 OF 3
fn try_recursive_macros() {

    macro_rules! write_html {
        ($w:expr, ) => (());

        ($w:expr, $e:tt) => (write!($w, "{}", $e));

        ($w:expr, $tag:ident [ $($inner:tt)* ] $($rest:tt)*) => {{
            write!($w, "<{}>", stringify!($tag));
            write_html!($w, $($inner)*);
            write!($w, "</{}>", stringify!($tag));
            write_html!($w, $($rest)*);
        }};
    }

    let mut out = String::new();

    write_html!(&mut out,
        html[
            head[title["Macros heading"]]
            body[h1["Macros body text"]]
        ]);

    assert_eq!(out,
        "<html><head><title>Macros heading</title></head>\
         <body><h1>Macros body text</h1></body></html>");

    println!("HTML output is: {}", out);
}
// END - TRY RECURSIVE MACROS PART 3 OF 3

// START - TRY ERRORS PART 3 OF 3
fn try_errors() {

    struct Info {
        name: String,
        age: i32,
        rating: i32,
    }

    fn write_info(info: &Info) -> io::Result<()> {
        let mut file = try!(File::open("my_fake_file.txt"));

        try!(writeln!(&mut file, "name: {}", info.name));
        try!(writeln!(&mut file, "age: {}", info.age));
        try!(writeln!(&mut file, "rating: {}", info.rating));

        return Ok(());
    }

}
// END - TRY ERRORS PART 3 OF 3

#2

Of course you have a problem - there is an std::io::prelude::Write (the trait std:io::Write), which conflicts with your import of std::fmt::Write. You need to give them different names (say import std::fmt and refer to that trait as fmt::Write)/


#3

Thanks for your response @arielb1.

Whilst it makes sense to change from use std::fmt::Write; and instead change it to use std::fmt;, unfortunately when I prefix all occurrences of write! by changing them to fmt::Write::write!, it gives the following errors (it appears that module prefixes within a macro block are not allowable):

error: expected macro name without module separators
fmt::Write::write!($w, "<{}>", stringify!($tag));
error: expected macro name without module separators
($w:expr, $e:tt) => (fmt::Write::write!($w, "{}", $e));

#4

https://doc.rust-lang.org/book/advanced-macros.html#scoping-and-macro-import/export


#5

Because you just need to import the trait to use the methods, and not actually use the Write name itself, you can just import it as a different name (like FmtWrite instead of Write) to fix the problem.

Try using:

use std::fmt::Write as FmtWrite;

instead of:

use std::fmt::write;

This just imports it as a different name (FmtWrite) so as to avoid the conflict with std::io::Write.


#6

@daboross thanks that worked perfectly!