Use Outer Reference Inside Module - rust

Consider:
struct Outer { ... }
mod Inner {
let outer: Outer { ... }
}
How does one "import" Outer into Inner? As it stands, Rust will complain no external crate "Outer".
Attempted solutions include mod { use Outer; ... }.

First, in Rust, you can always use qualified paths. That means if Outer as a type didn't work, use Outer will not work either.
To access the parent module, use the keyword super:
let outer: super::Outer;
It refers to the parent module, so we say "from the parent module, take Outer".
Another way, not recommended for this case but useful to know in other cases, is to track the parent module path down from the crate root. Assuming your parent module is inside the module b that is inside a, you can write crate::a::b::parent_module::Outer.

Related

How to `use` function scoped structs?

Consider the following contrived example:
mod Parent {
fn my_fn() {
struct MyStruct;
mod Inner {
use super::MyStruct; //Error: unresolved import `super::MyStruct`. No `MyStruct` in `Parent`
}
}
}
How can I import MyStruct here from the inner module?
Motivation
While the above is code that you'll never write manually, it is code that is useful to generate. A real-world use-case would be a derive-macro. Let's say I want this:
#[derive(Derivative)]
struct MyStruct;
Now it's useful to isolate the generated code in its own module, to isolate the generated code from the source code (e.g. to avoid naming collisions, leaking of use declarations, etc.). So I want the generated code to be something like this:
mod _Derivative_MyStruct {
use super::MyStruct;
impl Derivative for MyStruct { }
}
However, the example above fails if the struct is defined in a function, due to the problem at the top. e.g. this won't work:
fn my_fn() {
#[derive(Derivative)];
struct MyStruct;
}
as it expands into:
fn my_fn() {
#[derive(Derivative)];
struct MyStruct;
mod _Derivative_MyStruct {
use super::MyStruct; // error
impl Derivative for MyStruct {}
}
}
This is especially troublesome for doctests, as these are implicitly wrapped in a function. E.g. this will give the unresolved import problem:
/// Some interesting documentation
/// ```
/// #[derive(Derivative)]
/// struct MyStruct;
/// ```
Without the ability to refer to the outer scope, I either need to give up isolation, or require wrapping in modules at the call site. I'd like to avoid this.
This is issue #79260. I don't think there is a solution.
However, you can define the nested items inside an unnamed const (const _: () = { /* code */ };) instead of a module. This prevents name collisions and is the idiomatic thing to do in macros that need to define names. Do note however that this does not have a way to refer to items inside the const from outside it.

Can I import super:: from a mod that's inside a fn?

I realise that this is an extremely odd thing to do but I'm writing a macro I'd like to be usable in as many places as possible. Take the following code:
mod outer {
struct OuterTestStruct {}
fn do_thing() {
struct InnerTestStruct {}
mod inner {
use super::OuterTestStruct;
use super::InnerTestStruct;
}
}
}
This doesn't compile because of the use super::InnerTestStruct line. But the use super::OuterTestStruct line works fine, so my assumption here is that super skips over the fn context and goes straight to the parent mod.
Is there any way I can get a reference to InnerTestStruct from inside mod inner? Especially without knowing any context beforehand (i.e. imagine a macro invocation inside fn do_thing(), it isn't going to know it's inside a fn)
Is there any way I can get a reference to InnerTestStruct from inside mod inner?
No, super will refer to the encompassing module, not the function scope. There is no path that can name InnerTestStruct as far as I'm aware.
Since you mention macros specifically, the Rust API Guidelines warns against this exact case:
Item macros work anywhere that items are allowed
Rust allows items to be placed at the module level or within a tighter scope like a function. Item macros should work equally well as ordinary items in all of these places. The test suite should include invocations of the macro in at least the module scope and function scope.
As a simple example of how things can go wrong, this macro works great in a module scope but fails in a function scope.
macro_rules! broken {
($m:ident :: $t:ident) => {
pub struct $t;
pub mod $m {
pub use super::$t;
}
} }
broken!(m::T); // okay, expands to T and m::T
fn g() {
broken!(m::U); // fails to compile, super::U refers to the containing module not g
}
The only fix I know is to introduce another module:
mod outer {
struct OuterTestStruct {}
fn do_thing() {
mod middle { // <----------------
struct InnerTestStruct {}
mod inner {
use super::super::OuterTestStruct;
use super::InnerTestStruct;
}
}
}
}

Why do I need to "use std" in a module but not at the root of my crate? [duplicate]

I have the following code:
pub mod a {
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
}
I get errors when I compile this:
error[E0433]: failed to resolve. Use of undeclared type or module `std`
--> src/main.rs:4:24
|
4 | println!("{}", std::fs::remove_file("Somefilehere"));
| ^^^ Use of undeclared type or module `std`
However, removing the inner module and compiling the code it contains by itself works fine:
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
What am I missing here? I get the same errors if the module is in a separate file:
main.rs
pub mod a;
a.rs
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
By default, the compiler inserts extern crate std; at the beginning of the crate root (the crate root is the file that you pass to rustc). This statement has the effect of adding the name std to the crate's root namespace and associating it with a module that contains the public contents of the std crate.
However, in child modules, std is not automatically added in the module's namespace. This is why the compiler cannot resolve std (or anything that starts with std::) in a module.
There are many ways to fix this. First, you can add use std; in a module to make the name std refer, within that module, to the root std. Note that in use statements, the path is treated as absolute (or "relative to the crate's root namespace"), whereas everywhere else, paths are treated as relative to the current namespace (be it a module, a function, etc.).
pub mod a {
use std;
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
}
You can also use a use statement to import more specific items. For example, you can write use std::fs::remove_file;. This lets you avoid having to type the whole path to remove_file and just use the name remove_file directly within that module:
pub mod a {
use std::fs::remove_file;
#[test]
pub fn test() {
println!("{:?}", remove_file("Somefilehere")));
}
}
Finally, you can avoid using use altogether by prefixing the path with :: to ask the compiler to resolve the path from the crate's root namespace (i.e. turning the path into an absolute path).
pub mod a {
#[test]
pub fn test() {
println!("{:?}", ::std::fs::remove_file("Somefilehere"));
}
}
The recommended practice is to import types (structs, enums, etc.) directly (e.g. use std::rc::Rc;, then use the path Rc), but to use functions through an import of their parent module (e.g. use std::io::fs;, then use the path fs::remove_file).
pub mod a {
use std::fs;
#[test]
pub fn test() {
println!("{:?}", fs::remove_file("Somefilehere"));
}
}
Side note: You can also write self:: at the beginning of a path to make it relative to the current module. This is more often used in use statements, since other paths are already relative (though they are relative to the current namespace, whereas self:: is always relative to the containing module).
Nowadays, std is directly accessible from everywhere, so the code you showed is compiling as you would expect.
Furthermore, extern crate is no longer needed in Rust edition 2018. Adding a dependency to Cargo.toml makes the crate name directly available as a top-level identifier.

Why does a module with a submodule and a function with conflicting names work?

Here's the code I'm wondering about: (Rust Playground)
mod a {
pub mod b {
pub fn b() {
println!("b");
}
}
pub use self::b::b;
}
use a::b;
fn main() {
b();
b::b();
}
Why does b::b() work? Shouldn't the module b and the function b conflict? Does that mean that a::b is both a module and a function somehow?
Shouldn't the module b and the function b conflict?
No, because they're in different namespaces. They conflict in the same way "record" and "record" conflict in English: they're spelled and sound the same, but mean different things, and you can tell which is which based on context.
You can't call a module, so b() can't be talking about a module. Functions don't have child items, so b::b can't be talking about a function. This is important when you consider things like struct S(i32);. S is both a type (the structure type itself) and a function (the constructor).

Use of undeclared type or module `std` when used in a separate module

I have the following code:
pub mod a {
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
}
I get errors when I compile this:
error[E0433]: failed to resolve. Use of undeclared type or module `std`
--> src/main.rs:4:24
|
4 | println!("{}", std::fs::remove_file("Somefilehere"));
| ^^^ Use of undeclared type or module `std`
However, removing the inner module and compiling the code it contains by itself works fine:
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
What am I missing here? I get the same errors if the module is in a separate file:
main.rs
pub mod a;
a.rs
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
By default, the compiler inserts extern crate std; at the beginning of the crate root (the crate root is the file that you pass to rustc). This statement has the effect of adding the name std to the crate's root namespace and associating it with a module that contains the public contents of the std crate.
However, in child modules, std is not automatically added in the module's namespace. This is why the compiler cannot resolve std (or anything that starts with std::) in a module.
There are many ways to fix this. First, you can add use std; in a module to make the name std refer, within that module, to the root std. Note that in use statements, the path is treated as absolute (or "relative to the crate's root namespace"), whereas everywhere else, paths are treated as relative to the current namespace (be it a module, a function, etc.).
pub mod a {
use std;
#[test]
pub fn test() {
println!("{:?}", std::fs::remove_file("Somefilehere"));
}
}
You can also use a use statement to import more specific items. For example, you can write use std::fs::remove_file;. This lets you avoid having to type the whole path to remove_file and just use the name remove_file directly within that module:
pub mod a {
use std::fs::remove_file;
#[test]
pub fn test() {
println!("{:?}", remove_file("Somefilehere")));
}
}
Finally, you can avoid using use altogether by prefixing the path with :: to ask the compiler to resolve the path from the crate's root namespace (i.e. turning the path into an absolute path).
pub mod a {
#[test]
pub fn test() {
println!("{:?}", ::std::fs::remove_file("Somefilehere"));
}
}
The recommended practice is to import types (structs, enums, etc.) directly (e.g. use std::rc::Rc;, then use the path Rc), but to use functions through an import of their parent module (e.g. use std::io::fs;, then use the path fs::remove_file).
pub mod a {
use std::fs;
#[test]
pub fn test() {
println!("{:?}", fs::remove_file("Somefilehere"));
}
}
Side note: You can also write self:: at the beginning of a path to make it relative to the current module. This is more often used in use statements, since other paths are already relative (though they are relative to the current namespace, whereas self:: is always relative to the containing module).
Nowadays, std is directly accessible from everywhere, so the code you showed is compiling as you would expect.
Furthermore, extern crate is no longer needed in Rust edition 2018. Adding a dependency to Cargo.toml makes the crate name directly available as a top-level identifier.

Resources