How should I use `macro_export` for a custom module - rust

I have the following structure:
Inside queues.rs I have a #[macro_export],
#[macro_export]
macro_rules! queue {...}
but I am not sure how to import it in lib.rs.
What I have looks like this:
use crate::utils::queues::*;
// use it here
let mut service_queue: Queue<isize> = queue![];
This throws an error
error: cannot find macro `queue` in this scope
--> src/broker.rs:54:51
|
54 | let mut service_queue: Queue<isize> = queue![];
| ^^^^^
|
= note: consider importing this macro:
crate::queue
= help: have you added the `#[macro_use]` on the module/import?
What is the correct way to type #[macro_use] here, and import my custom macros from utils?

You can use the queue! macro from lib.rs using the #[macro_use] attribute (playground):
#[macro_use]
mod utils {
#[macro_use]
mod queues {
macro_rules! queue {
() => { vec![] };
}
}
}
pub fn foo() {
let _service_queue: Vec<isize> = queue![];
}
In your existing code you are using #[macro_export] which means it is declared in the crate root scope and not in the utils::queues module. In your error message you can see the compile is suggesting to consider importing this macro: crate::queue.
To access it in this case (playground):
mod utils {
mod queues {
#[macro_export]
macro_rules! queue {
() => { vec![] };
}
}
}
pub fn foo() {
let _service_queue: Vec<isize> = queue![];
}
Note that in either case the use crate::utils::queues::*; statement does not help, in fact you'll get an unused_imports warning (unless you happen to have other items declared in that module).
There is also this trick (playground):
pub mod utils {
pub mod queues {
macro_rules! queue {
() => { vec![] };
}
pub(crate) use queue;
}
}
pub fn foo() {
let _service_queue: Vec<isize> = crate::utils::queues::queue![];
}
Aside: if you plan to use the macro in other crates and you wish to retain the module hierarchy (i.e. access from my_crate::utils::queues::queue! from my_other_crate) there is a (convoluted) way to do that.

Related

How to export function and macro with the same name?

Is it possible to export a function and a macro with the same name from a module?
Example lib.rs
mod log;
fn foo() {
log::info!("");
log::info("");
}
In log.rs:
Using pub(crate) use info; conflicts with pub fn info() { .. }
Using #[macro_export] and #[macro_use] doesn't allow namespaces
Macros and functions belong to different namespaces so two with the same name should happily coexist. This compiles (playground):
macro_rules! info {
($v:expr) => {}
}
fn info(v: &str) { }
However, trouble seems to arise when when trying to make them public from within a module. Exporting the macro as shown in How do I use a macro across module files? seems to conflict somehow with the function declaration (playground):
mod log {
macro_rules! info {
($v:expr) => {}
}
pub(crate) use info;
pub fn info(v: &str) { }
}
error[E0255]: the name `info` is defined multiple times
--> src/lib.rs:8:5
|
6 | pub(crate) use info;
| ---- previous import of the value `info` here
7 |
8 | pub fn info(v: &str) { }
| ^^^^^^^^^^^^^^^^^^^^ `info` redefined here
|
= note: `info` must be defined only once in the value namespace of this module
help: you can use `as` to change the binding name of the import
|
6 | pub(crate) use info as other_info;
| ~~~~~~~~~~~~~~~~~~
I do not know if this is a bug or intended behavior. Either way it is confusing.
A workaround that I found was to declare the function in a separate module and then re-export it by wildcard in the original module (playground):
mod log {
mod imp {
pub fn info(v: &str) { }
}
pub use imp::*;
macro_rules! info {
($v:expr) => { }
}
pub(crate) use info;
}
You can do it the other way (i.e. putting the macro in a separate module) but the compiler strangely yields a warning that it didn't re-export anything even when it did (playground).

When if ever do pub and pub(super) have different semantics?

Rust has support for both pub and pub(super). pub makes it so the parent module can access an item... and pub(super) seems to also do exactly the same thing. I've tried playing with the example below, and swapping pub and pub(super) seems to have no effect:
#![allow(dead_code)]
mod outer {
pub(super) fn outer_foo() { inner::inner_foo(); }
mod inner {
pub(super) fn inner_foo() { println!("hello world!"); }
}
}
fn top_level_foo() { outer::outer_foo(); }
Why would you ever use one over the other?
Playground link.
In your example, if you change your inner module to be public, then the difference would become clear.
For example, this works because outer::inner::inner_foo is visible from the main module:
#![allow(dead_code)]
mod outer {
pub(super) fn outer_foo() { inner::inner_foo(); }
pub mod inner {
pub fn inner_foo() { println!("hello world!"); }
}
}
fn top_level_foo() { outer::outer_foo(); }
fn main() {
outer::inner::inner_foo();
}
If you kept inner_foo as pub(super) it would've been only visible from the outer module (because super refers to the super-module, outer in the case of something inside inner), hence the above code wouldn't compile and you would see this error:
|
13 | outer::inner::inner_foo();
| ^^^^^^^^^ private function
|
note: the function `inner_foo` is defined here
Notice that pub can also take crate as an argument, making the function public within the crate itself, but not to other crates.

How to insert Expr when using VisitMut?

I'm currently using syn following an example to create an AST that can be mutated. I understand that I can modify the node I'm travesing (as shown below in my current code) but
I'm curious if I can add some code in between the current node and the next node. Is the syn crate capable of this?
use syn::visit_mut::{self, VisitMut};
use syn::Expr;
#[derive(Debug)]
struct MyStruct;
impl VisitMut for MyStruct {
fn visit_expr_mut(&mut self, node: &mut Expr) {
if let Expr::MethodCall(expr) = &node.to_owned() {
// I can modify the existing node like so:
*node = parse_quote!("// Hello World");
// How could I add something after this node and before the next?
}
}
}
pub fn create() {
let current_dir = std::env::current_dir().expect("Unable to get current directory");
let rust_file = std::fs::read_to_string(current_dir.join("src").join("lib.rs")).expect("Unable to read rust file");
let ast = syn::parse_file(&rust_file).expect("Unable to create AST from rust file");
MyStruct.visit_file_mut(&mut ast);
}
Edit to show use case:
The file I'm currently parsing looks like:
#[macro_use]
extern crate foo;
mod test;
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
}
Let's say that when I read the AST, I want to add another mod and another handle for it like so:
#[macro_use]
extern crate foo;
mod test;
mod store;
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
handle.add_class::<Store::store>();
}
As I commented, it highly depends on what you want to insert. Because you can't just insert anything before or after node easily.
For your specific case, you could use parse_quote! to produce an ExprBlock.
*node = parse_quote!(
{
#expr;
handle.add_class::<Store::store>();
}
);
Which with the following input:
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
}
Would produce this output:
fn init(handle: foo::InitHandle) {
{
handle.add_class::<Test::test>();
handle.add_class::<Store::store>();
};
}
(Note I have reformatted the output, to be prettier)
Alternatively, you could override visit_block_mut() instead. That way you'd have access to stmts: Vec<Stmt>, and would be able to insert before and after a Stmt. The downside is that by doing it that way, you wouldn't be able to easily visit all Exprs, as by using visit_expr_mut().

Using conditionally compiled module under `cfg` macro

I wonder how to use a conditionally compiled module under cfg! macro. I am trying this:
pub fn f() { ... }
#[cfg(feature = "x")]
pub mod xmodule {
pub fn f() { ... }
}
pub fn test() {
if cfg!(feature = "x") {
xmodule::f();
} else {
f();
};
}
It works fine when I compile it with cargo check --features x, but if I don't enable the feature it fails with the following error:
use of undeclared type or module `xmodule`
Am I doing something wrong or the compilation is not smart enough to understand that the module should not be used if the feature is not set?
While the #[cfg] attribute will conditionally compile code, cfg! is gives the equivalent boolean value (e.g. true if a feature is enabled, false otherwise). So your code essentially compiles into:
pub fn test() {
if false { // assuming "x" feature is not set
xmodule::f();
} else {
f();
};
}
Therefore both branches must still contain valid code, even if only one is ever run.
To get actual conditional compilation, you may do something like this:
pub fn test() {
#[cfg(feature = "x")]
fn inner() {
xmodule::f()
}
#[cfg(not(feature = "x"))]
fn inner() {
f()
}
inner();
}
Playground example
Or you can use a third-party macro like cfg-if:
use cfg_if::cfg_if;
pub fn test() {
cfg_if! {
if #[cfg(feature = "x")] {
xmodule::f();
} else {
f();
}
}
}
Playground example

`use` statement necessary for trait not used directly in this source file: why?

In the code below, removing the second line will result in a compilation error saying:
type `std::io::net::tcp::TcpListener` does not implement any method in scope named `listen`
Since I am nowhere directly using Listener (even though std uses it internally), why do I need to specify it?
use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
fn handle_client(mut stream: TcpStream) {
// ...
}
fn main() {
let args = std::os::args();
println!("{}", args);
let listener = TcpListener::bind("127.0.0.1", 80).unwrap();
let mut acceptor = listener.listen().unwrap();
for stream in acceptor.incoming() {
spawn(proc() {
handle_client(stream.unwrap());
});
}
}
It was a design decision of the language to require explicit use of traits that implement methods. For example in the following code:
use my::A;
use my::B;
mod my {
pub trait A {
fn foo(&self);
}
pub struct B;
impl B {
pub fn bar(&self) {
println!("Called `bar`.");
}
}
impl A for B {
fn foo(&self) {
println!("Called `foo`.");
}
}
}
fn main() {
// Requires "use my::B".
let b = B;
b.bar();
// Requires "use my::A".
b.foo();
}
I believe the motivation for this is heavily due to the fact that currently there is no seamless way to support multiple traits with the same method name. There is a lot of work being done to traits atm however https://github.com/rust-lang/rfcs/blob/master/active/0024-traits.md.
Listener is a trait, and your code uses its listen method, implemented by TcpListener
http://static.rust-lang.org/doc/master/std/io/trait.Listener.html

Resources