-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC to require impl MyStruct
to be nearby the definition of MyStruct
#155
Conversation
I have found this feature useful in my library to encompass the state of a foreign library and do dependency injection in a clean way. Here's a snippet of how its used: mod a
{
pub struct Bitmap
{
priv_field: () // note this field
}
impl super::Core
{
pub fn create_bitmap(&self) -> Bitmap
{
Bitmap{ priv_field: () }
}
}
}
pub struct Core;
impl Core
{
pub fn init_library() -> Core
{
Core
}
}
fn main()
{
let core = Core::init_library();
let bmp = core.create_bitmap();
} I suppose I could work around this by using a macro that looks like this: mod a
{
pub struct Bitmap
{
priv_field: ()
}
pub mod export
{
cross_impl!
{
impl Core
{
pub fn create_bitmap(&self) -> Bitmap
{
Bitmap{ priv_field: () }
}
}
}
// Expands to:
/*
pub trait _AnonTrait_XXXXXX
{
fn create_bitmap(&self) -> Bitmap;
}
impl _AnonTrait_XXXXXX for Core
{
fn create_bitmap(&self) -> Bitmap
{
Bitmap{ priv_field: () }
}
}
*/
}
}
pub use a::export::*;
pub struct Core; But if I can do this via a macro, what prevents the compiler from doing it? |
@SiegeLord if you add any static methods to your If you use non-anonymous traits as described, you cannot have static methods that are accessible as Edit: I think it doable to simply prevent static methods in Edit2: If we were to take this approach, the pretty error messages would be missing in the currently-broken cases, which would still be a bug, though a minor one. |
```` | ||
which @Ryman noticed in https://github.com/rust-lang/rust/issues/15060 . It is | ||
not clear to me why this one fails, since the failure is in typeck, which I am | ||
not familiar with. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized this has nothing to do with typeck, that's just where "module instead of type" is noticed. The reason that Vec
in impl<U: T> Vec<U>
resolves to a module here rather than a type is in Resolver::def_for_namespace()
, which is eventually called from Resolver::resolve_crate()
, the final function call of Resolver::resolve()
. (The purpose of resolve_crate()
is to go through once every struct
and impl
has been seen to resolve things completely.)
The way that def_for_namespace()
works is to try and return a DefType
first, which it will only find if the type Vec
had been defined somewhere. Failing that, try and return a DefMod
, which will be defined if an impl Vec
or struct Vec
had occured somewhere. Failing that, return None.
Notice that at no point are imports used when resolving Vec
. This could happen when resolving Vec::from_slice()
later on, in Resolver::resolve_module_path()
, which first tries to find Vec
in the current module, then failing that, resolves imports. (In fact this would not happen, since Vec
would be found in the current module, as a module rather than type, thanks to the impl Vec
and the anonymous trait module it creates.) But this doesn't even have a chance to happen, since, resolve_module_path()
is never called when resolving impl Vec
; impl
blocks and functions become different beasts during the initial build_reduced_graph()
run, so different code paths are taken in resolve_crate()
. (And this makes sense, since when resolving impl Vec
in resolve_crate()
, we know that Vec
will is in the module because we added it in build_reduced_graph()
as an anonymous trait module. So there is no point in resolving imports.)
@SiegeLord I notice that in Edit: In fact, I don't think there's a way (short of seriously refactoring resolve) to handle static methods for anonymous traits on elsewhere-defined structs. And I don't want to say "only non-static methods on impls, unless the struct is defined in the same file" because that's an extremely specific rule to memorize and clearly originates in compiler limitations. Let's not be like certain other languages which have a bajillion such rules :) My feeling for simplicity's sake is to go with the original RFC, sans the " |
👍 Even if resolve handled it well, I think it'd be best to restrict anonymous impls to the defining module. |
I'd be interested in a move in the opposite direction .. fewer restrictions on what can be impl'd where, and the option of duck type interfaces to cut down on the noise when dealing with single function traits |
…tion of `MyStruct` This fixes #15060 and #3785.
Pushed changes which fix my own line comments (remove requirement to have |
+1, language simplification. |
I'm happy to implement this if it gains support. There are two new error messages to be added:
We can detect if I'm no good at bikeshedding, so can I get some suggestions for these? |
I am of mixed minds. On the one hand, this is the conservative choice, works around the various resolve bugs, and can easily be extended later. It also seems to satisfy a sort of encapsulation concern, in that it kind of makes sense for "inherent methods" to be defined close to the type in question. On the other hand, I think it is frequently useful to be able to split apart the methods on a type. @SiegeLord brings up an interesting usage I hadn't considered. I frequently find I want to define a core type and then define methods related to (e.g.) type checking in the type checker and other things somewhere else. On the other other hand, both of these use cases can readily be accommodated with traits. One specific concern I have is that violates the rule of thumb that if an action is legal in module M, it is legal in submodules of M. I think this is a good rule, since it ensures that you can always split up a module M into multiple submodules when it gets too complicated. (Still, as before, you can work-around it with traits.) (I am also a bit disappointed in the idea of choosing our RFCs based on resolve bugs, rather than resolving on what we think the rules should be and filing bugs where we deviate.) |
@nikomatsakis To me it's just a question of resourcing and priorities. I agree that what you describe is the best behavior, I just want to make sure we have something shippable for 1.0. |
An example of the modularity that @nikomatsakis says is useful appears in @jeremyletang's rgtk. Combining all the |
Another (relatively minor) issue: rust-lang/rust#16398 |
I use |
In particular: * The RFC associated with rust-lang#127 should have had a link to rust-lang#19 as well (and has been assigned RFC rust-lang#19); it also was revised to match the markdown href style of other RFCs. * RFC rust-lang#34 needed its header entries filled in, * RFC rust-lang#123 had a typo in its header, and * RC rust-lang#155 was revised to match the markdown href style of other RFCs.
This fixes #15060 and #3785.