Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I have to expose a macro implementation's 'use' in the client library?

Tags:

macros

rust

I'm trying to use a macro I've created in a separate module. With reference to this SO question, I've imported a macro fine. However it seems that I have

Update to add macro implementation

lib.rs

#![macro_use]
use std::fmt;
use std::ffi::CString;
use std::ffi::CStr;
use std::str;
extern crate libc;

pub enum DbaxError {
    DBAXException(String)
}

 #[macro_export]
macro_rules! dbax_call_test {
    ( $func : expr ) => {
        {
            let call_c_func = unsafe { dbax_function(CString::new($func).unwrap().as_ptr(),0) };
            match unsafe { getErrorCode() as i32 } {
                0 => Ok("Done".to_owned() + $func),
                _ => Err(DbaxError::DBAXException( str::from_utf8(unsafe { CStr::from_ptr(getError()) }.to_bytes()).unwrap().to_string()))
            }
        }

    }
}

and main.rs in a separate crate

// Import macro
#[macro_use] extern crate rustdbax;
// Import implementation details of macro
use rustdbax::*;
use std::ffi::CString;
use std::ffi::CStr;
use std::str;

fn main() {
    // Call my macro
    dbax_call_test!("DateAdd");
}

This works fine, but use std::* lines are all part of the implementation in the lib.rs.

Why do I have to expose the implementation's 'use' in the client library? Shouldn't rustc as part of it's expansion 'include' whatever was in the lib.rs?

like image 844
Delta_Fore Avatar asked Jun 19 '15 17:06

Delta_Fore


1 Answers

Because macro_rules! is a bit dumber than you might expect. It does not, for example, bring imports with it when it expands something. It's best to think of macro expansion as mostly just a dumb copy+paste job.

If you look at any reasonably well-written macro that depends on outside symbols, you'll see things like ::std::result::Result instead of Result. This is because the macro writer cannot depend on Result meaning what they expect in the expansion context. So the first step is to qualify paths absolutely.

The second thing to know is that every macro expansion gets a $crate substitution that is the path to the crate in which the macro was defined. You can use this to access, for example, DbaxError as $crate::DbaxError.

Finally, you're in luck; given the expansion, you can cheat a little and just add the use items inside the expansion:

#[macro_export]
macro_rules! dbax_call_test {
    ($func: expr) => {
        {
            use std::ffi::CString;
            use $crate::dbax_function;
            let call_c_func = unsafe { dbax_function(CString::new($func).unwrap().as_ptr(),0) };
            // ...
        }
    }
}
like image 172
DK. Avatar answered Nov 15 '22 08:11

DK.