-
-
Notifications
You must be signed in to change notification settings - Fork 14.3k
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
MyTraithaving a trait methodfoo_function(…) - a submodule
my_submodulewithin whatever module containsMyStruct. - an implementation of
MyTraitforMyStruct, which necessarily has an implementation for theMyTraitfoo_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.