Hi, everyone!
Now I try to implement a macro rule that will use a trait defined at another file. I can import the trait explicitly when I use this macro. But this way couldn't make sure that I don't forget to import the trait. Although the compiler would complain to me.
So I want to know how to import a trait automatically when using the macro rule?
Here is my macro:
// filename: procedure.rs
#[macro_export]
macro_rules! make_proc {
( $name:literal, |_| { $exps:expr }) => {
Procedure::new($name, 0, |_| $exps)
};
( $name:literal, $num:literal, |$($arg_name:ident:$arg_type:ty),+| { $exps:expr } ) => {
Procedure::new($name, $num, |args| {
let mut idx = 0usize;
$(
let $arg_name = <$arg_type>::try_from(&args[idx]).unwrap();
#[allow(unused_assignments)]
{
idx += 1;
}
)+
$exps
})
};
}
And the try_from
definition is at another file value.rs
beside procedure.rs
.
Here is the TryFromValue
trait:
// filename: value.rs
pub trait TryFromValue: Sized {
fn try_from(v: &Value) -> Result<Self, TypeError>;
}
More details at here and here if you need.
In this particular case, I'd probably use the fully-qualified syntax to use the trait without importing it:
let $arg_name = <$arg_type as ::std::convert::TryFrom<_>>::try_from(&args[idx]).unwrap();
Alternatively, you can import the trait inside the code generated by the macro:
( $name:literal, $num:literal, |$($arg_name:ident:$arg_type:ty),+| { $exps:expr } ) => {
Procedure::new($name, $num, |args| {
use ::std::convert::TryFrom; // <-- HERE
let mut idx = 0usize;
$(
let $arg_name = <$arg_type>::try_from(&args[idx]).unwrap();
#[allow(unused_assignments)]
{
idx += 1;
}
)+
$exps
})
Oh, I should clarify that the try_from
isn't come from the std::convert::TryFrom
trait. It comes from my custom trait named TryFromValue
in the value.rs
file. And here is it:
// filename: value.rs
pub trait TryFromValue: Sized {
fn try_from(v: &Value) -> Result<Self, TypeError>;
}
Both value.rs
and procedure.rs
inside the same crate, the structure below:
reg_machine
|- machine
|- value.rs
|- procedures.rs
|- lib.rs
Here are my attempts:
// first one: absolute path, not work
( $name:literal, $num:literal, |$($arg_name:ident:$arg_type:ty),+| { $exps:expr } ) => {
Procedure::new($name, $num, |args| {
let mut idx = 0usize;
$(
let $arg_name: <$arg_type> = reg_machine::machine::value::TryFromValue::try_from(&args[idx]).unwrap();
// ^^^^^^^^^^^ here
#[allow(unused_assignments)]
{
idx += 1;
}
)+
$exps
})
// second one: relative path, not work too
( $name:literal, $num:literal, |$($arg_name:ident:$arg_type:ty),+| { $exps:expr } ) => {
Procedure::new($name, $num, |args| {
let mut idx = 0usize;
$(
let $arg_name: <$arg_type> = crate::machine::value::TryFromValue::try_from(&args[idx]).unwrap();
// ^^^^^ here
#[allow(unused_assignments)]
{
idx += 1;
}
)+
$exps
})
It looks like the key thing you're missing is $crate
which is always the root of the crate where the macro is defined, regardless of where it's used-- You should refer to the trait as $crate::machine::value::TryFromValue
when inside the macro.
2 Likes
Wow! Thanks for your help sincerely.
system
Closed
October 22, 2021, 6:30am
6
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.