-
Notifications
You must be signed in to change notification settings - Fork 38
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
Cleanup internal imports #243
Merged
konstin
merged 7 commits into
pubgrub-rs:dev
from
astral-sh:konsti/pubgrub-cleanup-internal-imports
Jul 31, 2024
Merged
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
05ad4cc
Cleanup internal imports
konstin e3edeff
Fix more lints
konstin cfbbfb5
typo
konstin 49bd023
typo
konstin cbea9dd
And in the test, too
konstin dd9b290
Revert internal api abstraction
konstin fc1d1bb
Revert "Revert internal api abstraction"
konstin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Well this is exactly the kind of cyclic imports that I’ve always wanted to avoid.
crate
importsinternal.core
which in turn importscrate
. I believe this lead into putting definitions and functions in a much less organized and maintainable way in the long term, because we don’t care where we put it and justpub(crate)
it.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 do find everything at the root very useful and convenient for outside callers of the library, or test files. Just not for the inside of the library itself.
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've reverted the internal import changes
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.
Sorry for the long wait time. I didn’t mean specifically
internal
just inside the library in general. The idea is to avoid cyclic imports, which are signs that something is not compartmentalized properly. Because a module that requires cyclic imports means that any other module that would need a functionality from this one, would also end up with the whole crate (all its modules) as a dependency. Which makes things like dead code elimination more complex, and other bad architecture proofs in general.So basically, I find it fine to have examples, benchmarks, external tests, ... to import everything from the new top level crate. But I’d like to avoid having the library modules (anything that is in the import path from the root lib.rs) to import
crate
module.That being said, I know I’m a bit stubborn on this, and I know this isn’t something very common in rust crates, so I’m willing to bend if you all think its better.
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've never gotten the hang of keeping my imports well structured. So I personally would not mind making it a cyclical ball of spaghetti. :-P On the other hand, I'm terrible at naming things and I never remember to fix documentation. This project is better for your efforts to rein in those bad habits of mine.
So whichever way we decide, I would deeply appreciate automation that insists on it. Something in CI, even just a grep for the "bad" patterns, or are there tools in the ecosystem for identifying cyclical imports.
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.
Cyclical ball of spaghetti sounds delicious! ahah. I wanted to explain my reasoning because every time something is cyclical, it makes everything more complex. Dependency solving is more complex, memory releasing for garbage collector is more complex, etc. I see no reason why import graph solving would not suffer from similar issues, potentially leading to longer compilation times or less optimal dead code elimination for compiled bundles.
But that’s just my hunch and it isn’t backed by any scientific proof or concrete testing using Rust and using our code base in particular. So take that with a grain of salt. And if you think it makes code reading and maintenance simpler I can’t argue against that.
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.
For concrete data, I guess compiling a rather simple example based on pubgrub could be used. To see if this change makes any impact on compile time and bundle sizes.
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.
Now that I think about it, it probably would not change anything for pubgrub, because its not like people would use only a subset of pubgrub, they basically would use one of the top level modules that do need everything in the lib. There isn’t any part of this lib that really stands on its own.
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.
Resolving symbols to the source location is comparatively simple for a compiler: You build an alias map from exported symbol to origin path or the local definition for each file once and then when looking up a symbol, you iterate over the alias until you find the source location. If i want to resolve
Foo
, i walk through the scopes until i find ause crate::Foo
, then i askcrate
/src/lib.rs
forFoo
, it has memorized it as coming fromcrate::foo
/src/foo.rs
due topub use foo::Foo
, then i go tocrate::foo
/src/foo.rs
and it tells me it has a localstuct Foo
, so i get my fully qualified pathcrate::foo::Foo
(and pub/priv checking along the way).I found cyclical imports often become unavoidable with more complex crates, e.g. between the error capturing some struct for context and that struct return the error in its methods. I haven't found cyclical imports being a signal for bad architecture.
There are something that are known to be costly to the compiler, notably large/many dependencies, proc macros (need to be compiled, executed and then rustc needs to compile the bulk of generated code), generics (rustc generates and processes monomorphized code for each instantiation, i.e. n different usages give us n times the code) and llvm in general.
I say we structure the code in the way that's easiest for us to maintain and move as much work as possible to the compiler. Pubgrub compile ridiculously fast compare to any other crate i've worked on (0.2s for incremental
cargo build
, and ~2s forcargo test
on my desktop) so i have zero worries about compile times.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.
Thanks for the detailed answer! Alright, don’t forget to "unrevert" your changes then.