Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visibility: Non-importable types in public function signature? #1351

Open
sgasse opened this issue Apr 19, 2023 · 4 comments
Open

Visibility: Non-importable types in public function signature? #1351

sgasse opened this issue Apr 19, 2023 · 4 comments

Comments

@sgasse
Copy link

sgasse commented Apr 19, 2023

Recently, I ran into an unfamiliar visibility situation with a library that I used. I could not find any mentioning of this in the reference so I would like to know if this is just a (historic) artifact or actually by design. It concerns types in the signature of public functions which cannot be imported themselves.

Here is an example. Let's consider a workspace with the following layout:

.
├── app
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── Cargo.toml
└── helper
    ├── Cargo.toml
    └── src
        ├── lib.rs
        └── utils.rs

4 directories, 6 files
// helper/src/lib.rs
use utils::Tool;

mod utils;

pub fn get_tool() -> Tool {
    Tool {}
}
// helper/src/utils.rs
#[derive(Debug)]
pub struct Tool;
// app/src/main.rs
use helper::get_tool;

fn main() {
    // Getting a `Tool` and debug-printing it works
    let tool = get_tool();
    dbg!(tool);
}

// Not possible because `Tool` cannot be imported here.
// fn modify_tool(t: Tool) -> Tool {
//     ...
// }

// error[E0603]: module `utils` is private
// use helper::utils::Tool;

// error[E0603]: struct `Tool` is private
// use helper::Tool;
# app/Cargo.toml
# ...
[dependencies]
helper = { path = "../helper" }

Described in words: We have a library helper which has a private submodule utils. The private submodule utils contains a public struct Tool which is used in the return type of the public function get_tool() -> Tool of the library. When using the library in the other crate app, we can call the public function get_tool() and obtain the type Tool which is public in a private module but leaked through the public function signature. However we cannot easily pass the type Tool across function boundaries because we cannot import it itself (there is probably some trickery possible with implicitly capturing the type in a closure, but not sure if we could still return it).

In the library where I found it, this was not intentional and the type was subsequently made public. However do some libraries use this intentionally? Should the visibility chapter of the reference include this advanced situation and some information when it makes sense to do so?

@bjorn3
Copy link
Member

bjorn3 commented Apr 19, 2023

However do some libraries use this intentionally?

Yes, sealed traits are often created by adding a super trait to the trait which can't be imported. This pattern is used in the standard library as well as in some other libraries.

mod sealed {
    pub trait Sealed {}
}

pub trait SealedTrait: sealed::Sealed {}


impl sealed::Sealed for u8 {}
impl SealedTrait for u8 {}

@sgasse
Copy link
Author

sgasse commented May 9, 2023

Ah thank you @bjorn3 for pointing out the use as sealed traits 🙏 Do you think it would be helpful to mention this in the respective chapter of the reference?

@bjorn3
Copy link
Member

bjorn3 commented May 9, 2023

Do you think it would be helpful to mention this in the respective chapter of the reference?

I think so.

@sgasse
Copy link
Author

sgasse commented May 10, 2023

Alright, then I will draft a PR for this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants