Description
How function names are resolved to concrete implementations on concrete struct and trait objects appears to be incorrect for a specific (but reasonable) corner case.
Suppose we have
- a concrete struct type
MyStruct
- a method
foo_function(…)
implemented onMyStruct
(i.e. withinimpl MyStruct{}
) - a trait
MyTrait
having a trait methodfoo_function(…)
- a submodule
my_submodule
within whatever module containsMyStruct
. - an implementation of
MyTrait
forMyStruct
, which necessarily has an implementation for theMyTrait
foo_function(…)
function. (It doesn't matter if it's the default impl offoo_function(…)
forMyTrait
.)
Which function the expression MyStruct::foo_function(…)
resolves to depends on whether the implementation of foo_function(…)
for MyType
is inside of sub_module
. See the code below.
I tried this code:
struct MyStruct{}
impl MyStruct {
pub fn bar_function(&self) {
// Which function is called in the following depends on the visibility of the concrete type's `foo_function`.
// This behavior is NOT exhibited if we move the concrete type's `foo_function` impl to the same module.
// Call the function on the concrete `MyStruct` instance. Which function is _actually_ called?
self.foo_function();
// Same as above but with explicit notation to emphasize we are naming the MyStruct version in the call.
MyStruct::foo_function(self);
}
// The following is an error if its counterpart in `my_submodule` exists.
// fn foo_function(&self){
// println!("a third possibility.");
// }
}
// Some of the `MyStruct` impl in a submodule, for example, another file to keep the codebase navigable.
mod my_submodule {
// pub(crate) // Has no effect on behavior in question.
use crate::{MyStruct};
impl MyStruct {
pub // Different behavior depending on whether `fn` is `pub` or not.
fn foo_function(&self) {
println!("The MyStruct fn");
}
pub fn baz_function(&self) {
// Resolves to the method immediately above this one in every case.
MyStruct::foo_function(self);
}
}
}
trait MyTrait {
fn foo_function(&self);
}
impl MyTrait for MyStruct {
fn foo_function(&self) {
println!("The trait impl fn.");
}
}
fn main() {
let my_concrete_struct: &MyStruct = &MyStruct{};
my_concrete_struct.foo_function();
my_concrete_struct.bar_function();
my_concrete_struct.baz_function();
}
I expected to see this happen:
In any context in which private methods of MyStruct
are normally visible, I expect MyStruct::foo_function
to resolve to the implementation of foo_function(…)
on MyStruct
regardless of the visibility of that implementation. In other words, I expect the statement MyStruct::foo_function(my_concrete_struct);
and my_concrete_struct.foo_function();
to print The MyStruct fn
whenever they are in the implementation of a method on MyStruct
. Moreover, I expect a pub
annotation on an implementation on MyStruct
to modify the visibility of the function outside of the implementation of MyStruct
while having no effect on its visiblity within a module.
Instead, this happened:
When the implementation of foo_function
on MyStruct
is pub
(i.e. pub fn foo_function(…)…
), the expression MyStruct::foo_function
does preferentially resolve to the implementation on MyStruct
regardless of where it is called from. However…
When the implementation of foo_function
on MyStruct
is not pub
, that function is inaccessible even to other methods of MyStruct
the implementations of which are outside of my_submodule
, but remain accessible to methods on MyStruct
defined within my_submodule
. There appears to be no "fully qualified name" which allows methods outside of my_submodule
to access the method inside my_submodule
nor a "fully qualified name" which allows methods inside my_submodule
to access methods outside of my_submodule
except by importing the trait and using MyTrait::foo_function(&self)
.
Apparently, declaring the foo_function
within my_submodule
as pub
makes it public not just within the struct but within the submodule as well. Likewise, declaring the foo_function
within my_submodule
without the pub
annotation makes it not only private to code outside of the implementation of MyStruct
but also private within my_submodule
. For other methods on MyStruct
defined within my_submodule
, the foo_function
within my_submodule
shadows any definition outside of my_submodule
.
Curiously, if there is an implementation of foo_function
on MyStruct
(as opposed to a trait implementation) outside of my_submodule
, that implementation cannot be shadowed by another implementation of foo_function
on MyStruct
defined within my_submodule
. One might expect otherwise considering the notation MyStruct::foo_function
is allowed to vary depending on scope.
A consequence of this behavior is that a trait fn impl can take precedence over a concrete type's fn impl when the concrete type's impl is not pub and in a submodule.
Meta
Doesn't appear to depend on compiler version.