-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Leaking private structs in public functions #85883
Comments
The type What you're probably referring to is that the type |
Yes, it is technically public, but in all practicality, it's private. As you say, if you change the visibility, it does indeed error because it would be bad to return a type that cannot be annotated by the caller, yet this is exactly what the code above does. So this should also be an error I am aware it's an intended feature for traits, but not for structs. |
Specifically, the error is "A private item was used outside its scope.". Does that description not fit this scenario? |
The following code compiles: use foo::bbb;
fn main() {
let bar = bbb(); // cannot specify any type signature for `bar`
println!("{}", bar.some_inner_field);
bar.some_method();
}
mod foo {
use bar::Bar;
pub fn bbb() -> Bar {
Bar {
some_inner_field: 42
}
}
mod bar {
pub struct Bar {
pub some_inner_field: u8
}
impl Bar {
pub fn some_method(&self) {
println!("called a public method of an unnameable type");
}
}
}
}
If this were changed for structs, it would make the language less consistent, since it is intended for traits (and maybe even for structs too, though I don't know of any use cases). Maybe someone with more experience on actual use cases for this or how it came to be could weigh in too. |
The reason I consider this a problem is because of hyperium/tonic#672 - notably https://docs.rs/tonic/0.4.3/tonic/transport/server/struct.Router.html#method.add_service this function returns I have submitted a PR to tonic to get the visibility changed, but I do feel strongly that Rust (or clippy) should flag it. If you do want to return a type but have it private, I think it should be behind a trait and with |
This is (ability to use un-nameable, but public types in a public API) a conscious design choice, I believe. There are valid use-cases for structs, too. For instance when used as an argument in a trait method, it can prevent implementors providing implementation. There's perhaps slightly less value in doing so for return types, however, especially as it should be possible to name the return type via the associated |
Once again, it's a feature for traits. But I see absolutely no use for a returned struct to be un-nameable. I think if you want to have that power, you can create a trait (with a similar technique to seal it), implement the trait for your private struct, then return At least that way, I can still name something without needing the explicit type. The tonic example I gave above does not use this method, and it makes it impossible to build a wrapper around it. |
Trying to use some unstable features to come up with a way to name the "private" type I ended up with an ICE: #![feature(unboxed_closures)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
use foo::bbb;
type Fun = impl Fn<()>;
static BBB: Fun = bbb;
type Out = <Fun as FnOnce<()>>::Output;
fn main() {
println!("{}", std::any::type_name::<Fun>());
println!("{}", std::any::type_name::<Out>());
let bar: Out = BBB();
}
mod foo {
use bar::Bar;
pub fn bbb() -> Bar {
Bar
}
mod bar {
pub struct Bar;
}
} |
I do agree that this sort of situation is something that should be prevented from happening accidentally.
This ICE is unrelated to the issue at hand. I've tried to reduce it and managed to come up with this: #![feature(unboxed_closures)]
#![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
type Fun = impl Fn<()>;
static BBB: Fun = bbb;
type Out = <Fun as FnOnce<()>>::Output;
fn bbb() {}
fn main() {
let bar: Out = BBB();
} I was trying to look around for an issue with a similar ICE and I think this might be #85113. |
@rustbot label +A-visibility +T-compiler +T-lang |
This works to name the type (if it is #![feature(min_type_alias_impl_trait)]
#![feature(impl_trait_in_bindings)]
type Out = impl core::any::Any;
fn main() {
println!("{}", core::any::type_name::<Out>());
let _v: Out = foo::bbb();
}
mod foo {
use bar::Bar;
pub fn bbb() -> Bar { Bar }
mod bar {
pub struct Bar;
}
} |
agree with OP on this issue for return types. the same thing shows up when re-exporting traits via pub use inner::Foo;
mod inner {
pub struct Bar;
pub trait Foo {
fn foo() -> Bar;
}
} |
Can we maybe add a warning for that? in rust-secp256k1 we accidentally exposed a function that accepts a semi private struct: https://docs.rs/secp256k1/0.21.0/secp256k1/struct.XOnlyPublicKey.html#method.tweak_add_check so it would be great if the compiler/clippy could've warn us |
I tried this code:
I expected to see this happen: Rust complain about the leaking of private structs.
Instead, this happened: It compiles, this has caused it to creep into some crates. For instance, this is in tonic 0.4.3
Fixing this would likely be a harsh breaking change (as I mentioned, it's already in at least 1 crate, possibly many more). So it would probably have to live across an edition change. It's possibly too late to get a fix in for Rust 2021.
For now, I've suggested it as a possible lint in clippy
The text was updated successfully, but these errors were encountered: