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

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.

Related

Expose struct generated from quote macro without appearing out of nowhere

How can I expose a struct generated from the quote macro in my derive macro without having to introduce a struct name out of the blue in my usage file (due to macro expansion)?
To illustrate the point, currently, my code looks something like this:
// "/my_derive/lib.rs"
// inside a derive macro function
let tokens = quote! {
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl #ident_name {
pub fn something() -> Vec<MyDeriveMacroInternalStruct> {
vec![MyDeriveMacroInternalStruct { variant: #ident_name::#variant_name, /*...*/ }, /*...*/]
}
}
};
tokens.into()
The usage of my code would look something like this:
use my_derive::MyDerive;
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<MyDeriveMacroInternalStruct> { // having to write that struct name that came out of nowhere bothers me
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
This is a condensed version of my actual code (process_data is in another file). To reiterate my question in light of the example, how can I access the struct without having it randomly appear out of nowhere (due to macro expansion)? To me the code unchanged is hard to understand, read, and change.
I would like to be able to do something like this:
use my_derive::{MyDerive, MyDeriveStruct};
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<MyDeriveStruct> { // importing the struct instead of magically appearing
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
Obviously the idea seems quite stupid, but there has to be a way around it (an arbitrary struct definition). If what I imagined isn't possible, is there some way to be more clear about where the random struct came from?
Actually I thought of something better. Your derive should probably be associated with a trait of the same name.
Add an associated type to your trait:
trait MyDerive {
type Output;
...
}
Then set the associated type when you impl the trait:
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl MyDerive for #ident_name {
type Output = MyDeriveMacroInternalStruct;
pub fn something() -> Vec<MyDeriveMacroInternalStruct> {
vec![MyDeriveMacroInternalStruct { variant: #ident_name::#variant_name, /*...*/ }, /*...*/]
}
}
Then you can refer to that associated type in return position or wherever:
use my_derive::MyDerive;
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<<Something as MyDerive>::Output> {
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
Note: the convention is for #[derive(Trait)] to correspond to an impl for the given Trait, but your proc macro crate can't export a trait directly for importing in your library code.
So generally the solution is to have two crates:
my-trait is the "library" crate which contains the MyTrait trait definition
my-trait-derive is the proc-macro crate which contains the derive macro code
my-trait has my-trait-derive as a direct dependency, and re-exports the proc macro from it:
// my-trait lib.rs
pub use my_trait_derive::MyTrait;
// macro and trait names can overlap as they're
// treated as different item kinds
pub trait MyTrait {
type Output;
fn something();
}
see how clap does it here (they also re-export the whole clap_derive)
Then a user can use your proc macro + trait like this:
use my_trait::MyTrait;
#[derive(MyTrait)]
enum Something {}
fn process_data() -> Vec<<Something as MyTrait>::Output> {
Something::something()
}
Older Answer
What I would do is create a trait MyDeriveOutput or something with whatever stuff you want exposed from MyDeriveMacroInternalStruct:
trait MyDeriveOutput {
fn variant() ...
}
And then generate an impl for each internal struct you create:
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl MyDeriveOutput for MyDeriveMacroInternalStruct {
// whatever
}
Then you can expose the trait and require it to be imported and used with impl Trait in return position:
use my_derive::{MyDerive, MyDeriveOutput};
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<impl MyDeriveOutput> {
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}

How should I use `macro_export` for a custom module

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.

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).

Rust tells me that an import is unused, and then in the very same breath tells me that I should make that import

Here's the code:
use crate::rooms::room::RoomInterface;
pub mod dogroom {
pub struct R;
impl RoomInterface for R {
}
}
Here's /rooms/mod.rs:
pub mod room {
// Irrelevant stuff
pub trait RoomInterface {
// stuff
}
// stuff
}
Here's what it tells me:
I find it quite arcane that I import the very same thing it wants me to import, and yet it doesn't work.
I've tried pretty much all the permutations of the use keyword, and I can't make it work. What's going on?
uses are scoped to the module that imports them, not the file they are in.
Move the import into the dogroom module:
pub mod dogroom {
use crate::rooms::room::RoomInterface;
pub struct R;
impl RoomInterface for R {
}
}
Alternatively, you might want the dogroom module to reuse everything from the parent module:
use crate::rooms::room::RoomInterface;
pub mod dogroom {
use super::*;
pub struct R;
impl RoomInterface for R {
}
}

Rust Submodule Behavior with Wildcard Use Declarations

I was trying to understand how (sub)module imports work when using wildcard paths. The simplest demonstration I could come up with is as follows, where two modules, or perhaps two crates, share the same module structure.
pub mod primary {
pub mod a {
pub mod b {
pub struct A(pub i32);
}
}
}
pub mod import {
pub use crate::primary::*;
// Compiles and executes fine with this commented out, but fails with
// "error[E0433]: failed to resolve: could not find `b` in `a`"
// otherwise. The error refers to the usage in the assert_eq macro
// pub mod a {}
}
fn main() {
assert_eq!(import::a::b::A(42).0, 42);
}
My general thought, was, since the first case, where the pub mod a {} is commented out works, the wildcard should expand all submodules the wildcard picks up into submodules in the path in which it is expanding. Is this not the case? If so, what's the appropriate way to think about?
The Use declarations doesn't have a lot of detail on this.
use with a * imports all the names, except for those that would conflict with names that already exist in the current module.
Compare:
pub mod primary {
pub fn f() {
println!("p::f");
}
}
pub mod import {
pub use crate::primary::*;
}
fn main() {
import::f();
}
which prints p::f to
pub mod primary {
pub fn f() {
println!("p::f");
}
}
pub mod import {
pub use crate::primary::*;
pub fn f() {
println!("import::f");
}
}
fn main() {
import::f();
}
which prints import::f.
This might seem obvious for functions and constants (it would otherwise make * very limited, making it impossible for upstream libraries to add any item without risking to break downstream users), but it might seem more confusing for modules.
You have to remember though that you can't define a module multiple times (ie. "reopen" a module). The following is illegal:
pub mod primary {
pub mod a {}
pub mod a {}
}
and fails with
error[E0428]: the name `a` is defined multiple times
--> src/lib.rs:3:5
|
2 | pub mod a {}
| --------- previous definition of the module `a` here
3 | pub mod a {}
| ^^^^^^^^^ `a` redefined here
|
= note: `a` must be defined only once in the type namespace of this module
You can solve this particular case by adding one more level:
pub mod primary {
pub mod a {
pub mod b {
pub struct A(pub i32);
}
}
}
pub mod import {
pub mod a {
pub use crate::primary::a::*;
}
}
fn main() {
assert_eq!(import::a::b::A(42).0, 42);
}
A wildcard import creates aliases to all items on the top level of the module.
In your example, since primary contains only one item, a, the wildcard import creates an alias import::a, which refers to the module primary::a.
Whenever conflicts arise, explicitly named items are given higher precedence than items that were imported via a wildcard. Effectively, you can shadow a wildcard import by declaring a new item or by importing it by name from another module.
Each mod declaration declares a different module. There is no implicit merging of items in modules that happen to have the same name or alias.

Resources