Discourage optional dependencies turned on by default features #204
Replies: 10 comments
-
I think I mostly agree with your analysis with respect to the downside of putting optional dependencies in default features, but I definitely do not agree with your conclusion that this should be elevated to the level of an API guideline. I think whether one includes optional dependencies by default or not should largely be decided on a case by case basis. The API guidelines should definitely not make a recommendation that runs contrary to situations like that IMO.
I don't really understand why you say this. Could you please elaborate? |
Beta Was this translation helpful? Give feedback.
-
Somewhat agreed, in that, regex cannot be expected to change this default now. But had we a time machine, regex could have been more light-weight if they didn't compile by default, and people who needed various additional support turned it on as required. Performance concerns I'm a bit on the fence about, because arguably, whether you need your regex to be performant depends greatly on what you're doing with them, whether they're in a hot path or not. People who do need extra performance out of regex probably should be determining that based on actual profile analysis, not a premature optimization that pulls in additional dependencies.
Most of the guidelines should still be considered on a case-by-case basis, that's why they're guidelines, not rules. The idea is that people should read the guideline, understand why it is defined, and hopefully use that guideline to make the right choice for their crate.
If you depend on a crate you don't maintain yourself, and that crate uses yet-another crate which has optional dependencies, and makes no positive assertions about what features it requires, then by default, it pulls in the default features, and in turn, the default requirements, even if nothing in your crate, or the crate you're consuming, makes use (or benefits from) that feature being present. In turn, that means by me mandatorially depending on another crate, means I'm stuck lumping all its optional features' dependencies in my graph, I can't opt-out of them, because there's no communicative "no-default-features", I can't ask my dependencies to ask their own dependencies to have no-default-features. At least, not as far as I'm aware. |
Beta Was this translation helpful? Give feedback.
-
No. Even with a time machine I would make the same decision. Unicode should absolutely be enabled by default and so should the performance optimizations. They are good defaults to settle on because the extra dependencies required are overall quite small, and namely, not everyone has the same preferences as you with respect to keeping dependencies small. You say a smaller dependency tree is better by default, but I can just as easily say that correctness (Unicode features) and performance are better defaults. And that's my point. It's a judgment call. I would be okay of the guidelines discussed this trade off, but I am still firmly opposed to a specific recommendation.
Oh, okay. Sure, I would consider the non-propagation of relevant features a bug. I would be in favor of an API guideline that recommended re-exporting dependency features when it makes sense to. |
Beta Was this translation helpful? Give feedback.
-
IME, a large quantity of regex people write is likely unicode agnostic. I would be fine with regex barfing saying I need to turn on unicode support to use the unicode-specific syntax extensions. But that said, the dep weight of unicode support in regex is quite slim. It may help to understand that I'm suffering this in a linux-vendor context, which substantially changes a lot of things about how dependencies work in general ( Because I have to consider all optional deps, and test deps, in a somewhat recursive fashion, in ways that lead to a much larger graph anyway than you'd ever see in Cargo.lock ). But it also means I'm seeing problems that others are unlikely to see by default. ( Rust packaging in general has a lot of weaknesses here right now, but that's what I'm trying to push on improving )
Perhaps a more general guideline about keeping dependencies slim, and about providing necessary toggles to make it easier to keep dependencies slim. |
Beta Was this translation helpful? Give feedback.
-
Although this doesn't apply to the regex example, rust-lang/rfcs#1787 seems worth mentioning. Right now I think I agree that we shouldn't have a firm API guideline about this, but if we ever revive, accept and implement that RFC, then the tradeoffs would be in a place where I would support an API guideline (and this should probably be explicitly mentioned in any future RFC thread anyway). |
Beta Was this translation helpful? Give feedback.
-
@BurntSushi this is a bit of a side topic, but I noticed that "unicode support" ( in terms of dependencies ) is actually non-optional, as the dependency ( |
Beta Was this translation helpful? Give feedback.
-
I guess Unicode support is slightly weird in that enabling it doesn't incur any additional dependencies. |
Beta Was this translation helpful? Give feedback.
-
I think I would also appreciate a broad spectrum encouragement that, whenever one declared a dependency, one declared "default-features = false", and then explicitly declared all the mandatory features you need, and communicated all the optional ones that would be conceivably beneficial to a consumer of your crate via your own feature sets. At least then, from the perspective of dependency analysis, when performed at scale, all dependencies end up being 100% obvious where they come from, and where they're controlled, and it makes it far easier to reason about what is happening in that graph. (I'm currently packaging the dependencies required to be able to simply execute |
Beta Was this translation helpful? Give feedback.
-
Owch. I know this is getting a touch off topic, but for me, I rarely pay close attention to my dev-dependency tree. So that can often balloon quite a bit when compared to the normal dependency tree. I would usually not even bother with turning off features, unless I was getting bothered by compilation times. |
Beta Was this translation helpful? Give feedback.
-
There's also room for improvement with regards to having a few more dependency types, where this pain of dev-deps vs test-deps vs whatever deps vs optional deps could be made a bit better:
This is of course a side topic to this one, but these kinds of tools being at our disposal makes formulating useful recommendations more likely. |
Beta Was this translation helpful? Give feedback.
-
I have a bit of a concern here when it comes to keeping the dependency graph small by default.
If you have a crate with:
It seems it could be harmful to also declare that feature as "default".
By reason of, a crate that in turn consumes "foo" could inherently depend on this feature, but due to it being defaulted, it becomes an invisible requirement.
This in turn could mean that people consuming "foo" are getting dependencies included for features they're not even using.
And for API reasons, this naturally means that features once declared default, must always be declared default, or they risk breaking anything that inherently required that feature.
Defensive users can of course liberally use "no-default-features" to guard themselves against this, but the current state of affairs seems like an odd default to encourage.
When hand reading a Cargo.toml and seeing a dependency that is declared "optional", you might be prone to thinking it won't be needed by default.
But you have to make sense of the web of feature declarations to determine if it will be needed by default.
And currently, most of the time, the "optional" features are effectively hard requirements due to being defaulted.
Beta Was this translation helpful? Give feedback.
All reactions