I don't really understand why
-
impl<T: Internal> Trt for T
is allowed for a private traitInternal
, - but
impl<T: Internal> Dbl<T>
is not allowed ifInternal
is private, - yet
impl<T: hidden::Internal> Dbl<T>
works again ifhidden
is private andInternal
is public.
Is this really intended? And if yes, why? This makes things unnecessarily complex for me.
If all three cases were disallowed, I could understand it. But only disallowing the second case, while the first and third case are legal in Rust, doesn't make sense to me. But perhaps there is a good design rationale behind it?
In my real-life code, I don't want to make Internal
public because it exposes some private state.
P.S.: See also similar discussion: Why are private traits in public interfaces disallowed in this setting?
Full code example below:
mod api {
#[derive(Default)]
pub struct A {
internal: i32,
}
#[derive(Default)]
pub struct B {
internal: i32,
}
pub struct Dbl<T> {
pub one: T,
pub two: T,
}
trait Internal {
fn internal_ref(&self) -> &i32;
fn internal_mut(&mut self) -> &mut i32;
}
impl Internal for A {
fn internal_ref(&self) -> &i32 {
&self.internal
}
fn internal_mut(&mut self) -> &mut i32 {
&mut self.internal
}
}
impl Internal for B {
fn internal_ref(&self) -> &i32 {
&self.internal
}
fn internal_mut(&mut self) -> &mut i32 {
&mut self.internal
}
}
pub trait Trt {
fn increment(&mut self);
fn result(&self) -> i32;
}
// This is allowed:
impl<T: Internal> Trt for T {
fn increment(&mut self) {
*self.internal_mut() += 1;
}
fn result(&self) -> i32 {
*self.internal_ref()
}
}
// But this causes warning/error E0445:
impl<T: Internal> Dbl<T> {
pub fn sum(&self) -> i32 {
self.one.result() + self.two.result()
}
}
// Yet we can do the following:
pub trait SumViaTrait {
fn sum_via_trait(&self) -> i32;
}
impl<T: Internal> SumViaTrait for Dbl<T> {
fn sum_via_trait(&self) -> i32 {
self.one.result() + self.two.result()
}
}
// Or we can do:
mod hidden {
pub trait Internal {
fn internal_ref(&self) -> &i32;
fn internal_mut(&mut self) -> &mut i32;
}
impl Internal for super::A {
fn internal_ref(&self) -> &i32 {
&self.internal
}
fn internal_mut(&mut self) -> &mut i32 {
&mut self.internal
}
}
impl Internal for super::B {
fn internal_ref(&self) -> &i32 {
&self.internal
}
fn internal_mut(&mut self) -> &mut i32 {
&mut self.internal
}
}
}
pub trait Trt2 {
fn increment2(&mut self);
fn result2(&self) -> i32;
}
impl<T: hidden::Internal> Trt2 for T {
fn increment2(&mut self) {
*self.internal_mut() += 1;
}
fn result2(&self) -> i32 {
*self.internal_ref()
}
}
impl<T: hidden::Internal> Dbl<T> {
pub fn sum_via_priv_mod(&self) -> i32 {
self.one.result2() + self.two.result2()
}
}
}
fn main() {
use api::*;
let mut dbl = Dbl {
one: A::default(),
two: A::default(),
};
dbl.one.increment();
dbl.two.increment();
dbl.two.increment();
println!("{}", dbl.sum());
println!("{}", dbl.sum_via_trait());
println!("{}", dbl.sum_via_priv_mod());
}
Output:
3
3
3
Errors:
Compiling playground v0.0.1 (/playground)
warning: private trait `api::Internal` in public interface (error E0445)
--> src/main.rs:48:5
|
48 | / impl<T: Internal> Dbl<T> {
49 | | pub fn sum(&self) -> i32 {
50 | | self.one.result() + self.two.result()
51 | | }
52 | | }
| |_____^
|
= note: `#[warn(private_in_public)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>
warning: `playground` (bin "playground") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 4.48s
Running `target/debug/playground`