Rust is not the language for you if you don't like traits(github.com)
github.com
Rust is not the language for you if you don't like traits
https://github.com/marshallpierce/rust-base64/issues/213
14 comments
This sort of thing is why packages should always be namespaced. Just "base64" without any additional qualifiers makes it seem more official and definitive than it actually is.
I get the impression that some people think of crates.io as purely a place for distributing arbitrary Rust free software, while others think it's also a place where people are building something like a community-developed "batteries included" standard library for Rust.
I think a fair amount of unpleasantness in recent years has come from this disconnect: some people think that by publishing on crates.io you're making some promises (that they might not assume simply for, say, having a repository on github).
To me, using an extremely generic name like 'base64' does suggest that you're in the second camp, and so that you might expect to have a conversation that feels more like "we're equals building this project together" than "I'm the maintainer and my judgement is all that matters".
I think a fair amount of unpleasantness in recent years has come from this disconnect: some people think that by publishing on crates.io you're making some promises (that they might not assume simply for, say, having a repository on github).
To me, using an extremely generic name like 'base64' does suggest that you're in the second camp, and so that you might expect to have a conversation that feels more like "we're equals building this project together" than "I'm the maintainer and my judgement is all that matters".
Don't all languages, that have this kind of community developed packages, have the same problem? I expect golang, npm and python to have similar issues. Python has less issues because they have a bigger standard library but if you want to do anything advanced you need third party packages.
I feel like most of these issues stem from the very young age of Rust (2015 -> v1.0). Give it a few years and these problems will stabilize as the long term crates will survive and the short term will grow stale.
I feel like most of these issues stem from the very young age of Rust (2015 -> v1.0). Give it a few years and these problems will stabilize as the long term crates will survive and the short term will grow stale.
Golang doesn't because all packages are namespaces by domain name (and more often that not that domain namespace ja GitHub which has its own further "org" namespacing)
I think you are right. Though I think that one can have all the benefits of the “community-developed stdlib” with the namespaces approach as well — e.g. Java’s new Date/Time API actually comes from a third-party package called jodatime, it was just so good that it found itself implemented in the standard library of java, and then later on plenty other languages as well.
Rust has done this as well; hashbrown was an external package, but now powers HashMap and friends.
Great observation. This helps me reconcile the tension I feel between my “maintainers own us nothing” mentality and my disappointment with the way the base64 API issues have been handled.
Indeed. I have on occasion joked about starting a "crates-name squatting" business; pick a bunch of common names and buzzwords and hog them and give them up for $$, like domain squatters do.
I see the arguments against organization/namespacing in Rust (ala Maven or NPM) and I kinda shake my head. The reality is, crates.io is a crazy bazaar, people end up taking crate names and then abandoning their projects, leaving that name forever unusable.
e.g. at work we have an internal crate named "event" (on our internal repository) that conflicts with an abandoned public crate of the same name that hasn't had commits in 8 years. This leads to confusion if you get your cargo config wrong. But it's also kinda frustrating that this very generic name is ... lost to time now.
I see the arguments against organization/namespacing in Rust (ala Maven or NPM) and I kinda shake my head. The reality is, crates.io is a crazy bazaar, people end up taking crate names and then abandoning their projects, leaving that name forever unusable.
e.g. at work we have an internal crate named "event" (on our internal repository) that conflicts with an abandoned public crate of the same name that hasn't had commits in 8 years. This leads to confusion if you get your cargo config wrong. But it's also kinda frustrating that this very generic name is ... lost to time now.
Looks like "swmon" beat you to the punch.
https://crates.io/users/swmon
https://crates.io/users/swmon
It's neat how positively I can hear an idea like this and go "haha, what a fun idea," and then see it actually executed on and get a visceral "you are a terrible human" vibe, like that "Contact me if you want to use this name!" description makes me viscerally angry.
That's terrible.
Some prior discussion on namespacing packages in Rust:
https://internals.rust-lang.org/t/blog-post-no-namespaces-in...
https://internals.rust-lang.org/t/blog-post-no-namespaces-in...
I don’t like this crate either, it’s frustrating to use… but you don’t win any prizes for having a go at open source maintainers.
Create an alternative crate if you care enough, or pick a different crate to use. You can even literally pin to a previous version of this crate.
I have zero sympathy for folk coming along to a closed issue, which has been clearly closed and explained and trying to tell the maintainer they know better.
Create an alternative crate if you care enough, or pick a different crate to use. You can even literally pin to a previous version of this crate.
I have zero sympathy for folk coming along to a closed issue, which has been clearly closed and explained and trying to tell the maintainer they know better.
I have mixed feelings on this advice. On the one hand, it's not fair to criticize volunteer maintainers too harshly. On the other hand, the same person would probably see forking + marketing a fork as an attack—maybe not in the case of a small utility library, but certainly in the case of larger projects. A fork would be seen as an attempt to split or take over the community around the project because, well, that's exactly what it is. If you feel no sympathy for the users commenting on the issue you ought to feel not sympathy for the original maintainer in that scenario, and that seems like an unhealthy dynamic for open source as a whole. (For what it's worth I would feel no sympathy for maintainers that act like the one in this issue, but in general I would prefer people to spend some real effort on discussing and resolving disagreements before trying to fork anything important!)
Yeah you're right. I would never use this lib because I completely disagree with the maintainer in all regards here, from the coding choices to the motivations for doing what they did.
There's no point going to the issues tracker to tell him/her that though: just move on and pick a nicer library/maintainer.
There's no point going to the issues tracker to tell him/her that though: just move on and pick a nicer library/maintainer.
There are two guardrails: 0. not being unprofessionally, unnecessarily critical 1. while delivering and receiving necessary constructive feedback about usability. It's a signal suggesting narcissistic tendencies without an ability to cooperate or grow, to be against listening to constructive feedback or complaining about the gift of feedback if it wasn't delivered in precisely your perfectly arranged, expected format and process others aren't aware of.
It's also not that simple.
People forget that base64 has variant _all the time_, that leads to bugs.
So deprecating `decode` made sense.
But then what do you do instead?
a `decode_**` for each standard variant ... not really nice design
a `decode2(data, standard)` better but if you anyway need to import the standard and one additional thing, that thing could be a trait instead of a method and the `2` in the name is quite confusing
a braking change? definitely not worth it
a `prelude` module which exports the engine trait and reexports like `BASE64_STANDARD`? Well he did that.
People forget that base64 has variant _all the time_, that leads to bugs.
So deprecating `decode` made sense.
But then what do you do instead?
a `decode_**` for each standard variant ... not really nice design
a `decode2(data, standard)` better but if you anyway need to import the standard and one additional thing, that thing could be a trait instead of a method and the `2` in the name is quite confusing
a braking change? definitely not worth it
a `prelude` module which exports the engine trait and reexports like `BASE64_STANDARD`? Well he did that.
Why is decode_* not a nice design?
Because you are now encoding parameter choices in function names instead of making them parameters.
I mean it's not terrible design.
But having to do a `use base64::prelude::{Engine, BASE64_STANDARD}` is also not terrible design.
Nor is `use base64::engine::{Engine, general_purpose::STANDARD as BASE64}`, through it's less nice.
So given that there is already and equally good design there anyway, there is no reason to add additional code, especially if that code would make it easier to make a mistake when using it.
I guess if this would be designed now from scratch it would have something like a `decode(data, standard)` function, but `decode` is already in used (even through deprecated) so we can't have that.
I mean it's not terrible design.
But having to do a `use base64::prelude::{Engine, BASE64_STANDARD}` is also not terrible design.
Nor is `use base64::engine::{Engine, general_purpose::STANDARD as BASE64}`, through it's less nice.
So given that there is already and equally good design there anyway, there is no reason to add additional code, especially if that code would make it easier to make a mistake when using it.
I guess if this would be designed now from scratch it would have something like a `decode(data, standard)` function, but `decode` is already in used (even through deprecated) so we can't have that.
I don't see the problem here at all. What prevents somebody from writing a 5 line helper wrapper that provides the easier API either in their project or as a small crate if they want to share the helper between projects?
That's the benefit of having a more explicit API. Doing it the other way and making a generic API on top of a simplified one is harder.
That's the benefit of having a more explicit API. Doing it the other way and making a generic API on top of a simplified one is harder.
I agree, thinking in layers is super helpful here. I might prefer the higher-level, simpler API was part of the same project as the low-level API (esp. given the name recognition here), but when it isn't, I can go make my own high-level API.
I'm not a rust user; is there anything preventing someone from making a "base64-simple" crate which uses this base64 crate to make a higher-level API?
I'm not a rust user; is there anything preventing someone from making a "base64-simple" crate which uses this base64 crate to make a higher-level API?
No. It would likely be 4 lines in a 'utils' folder/file.
I was on the opposite side reading the github comments, notably because of my recent usage of python base64 but your (and GP) comments make a lot of sense, i wasn't using logic but habits. Thanks.
I was on the opposite side reading the github comments, notably because of my recent usage of python base64 but your (and GP) comments make a lot of sense, i wasn't using logic but habits. Thanks.
[deleted]
UI/API rule number one: the thing you do most frequently should be the easiest thing to do.
There’s a reason that words like “the” and “it” are not 60 characters long in any spoken language.
If you’re making it super verbose to do very common tasks, you’re fucking up your interface.
There’s a reason that words like “the” and “it” are not 60 characters long in any spoken language.
If you’re making it super verbose to do very common tasks, you’re fucking up your interface.
I tried to find a copy of Rusty Russell's API Design Manifesto to get another respected voice to back this opinion, but the most-linked to version of it now sits behind a Google sign-in, and (somewhat ironically) search results for Rust API documents/blog posts/reddit threads make other copies hard to find.
Here are the original lists:
How Do I Make This Hard to Misuse? - https://ozlabs.org/~rusty/index.cgi/tech/2008-03-30.html
What If I Don't Actually Like My Users? - https://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
How Do I Make This Hard to Misuse? - https://ozlabs.org/~rusty/index.cgi/tech/2008-03-30.html
What If I Don't Actually Like My Users? - https://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
Looks like we're out of luck, even with a Google sign-in:
> sweng.the-davies.net points to the site "sweng" which does not exist. Please contact the domain administrator.
There are archives, however: https://archive.ph/NZHmW
...and TIL that despite the name, this is not Rust-specific.
> sweng.the-davies.net points to the site "sweng" which does not exist. Please contact the domain administrator.
There are archives, however: https://archive.ph/NZHmW
...and TIL that despite the name, this is not Rust-specific.
I actually like this design choice. Having dealt with these kind of bugs, only to have to switch to the URL-safe base64 encoding, makes me really appreciate it.
It forces the developer to consider which configuration they want, and in that, actually realize that there is a difference between URL-safe and the standard version.
It forces the developer to consider which configuration they want, and in that, actually realize that there is a difference between URL-safe and the standard version.
That was a breaking change, and was a medium-sized headache, in that it broke some crates. As an original design decision it would not have been too bad, but as a breaking change, it was.
Apparently the author also thinks being respectful of others shouldn’t be a default
I think the author is just rally feed up by people not searching closed issues, opening duplicates and being annoying about the same already discussed issues again and again.
That doesn't make it good, but understandable.
Not reading or looking into the context is also a common HN I mean how many people commenting did look up the context?
And why is a small open source crate for a minor annoying design aspect even on hacker news just because it's called base64????????
That doesn't make it good, but understandable.
Not reading or looking into the context is also a common HN I mean how many people commenting did look up the context?
And why is a small open source crate for a minor annoying design aspect even on hacker news just because it's called base64????????
Don't read the rest of those github comments if you'd like to maintain respect for anyone involved.
I don't disagree with the idea that a relatively complicated base64 API seems painful but...
> Normally I agree with explicit being better than implicit, but I don't see the harm here. If there's no specific interoperability requirements, explicitly choosing a configuration isn't particularly important.
Pretty much the entire point of base64 is interop, so there would always be specific interop requirements.
And since a base64 library tends to be reflexive, a library's default tends to either "way too strict to consume any base64 you can find in the wild" or "so loose you can get a carrier strike group through" e.g. Ruby and Python will strip out any invalid character from the input before attempting a parse, python requires padding to be present but I'm not sure there's any way to make ruby's decode64 fail as long as you give it a string since it does not.
> Normally I agree with explicit being better than implicit, but I don't see the harm here. If there's no specific interoperability requirements, explicitly choosing a configuration isn't particularly important.
Pretty much the entire point of base64 is interop, so there would always be specific interop requirements.
And since a base64 library tends to be reflexive, a library's default tends to either "way too strict to consume any base64 you can find in the wild" or "so loose you can get a carrier strike group through" e.g. Ruby and Python will strip out any invalid character from the input before attempting a parse, python requires padding to be present but I'm not sure there's any way to make ruby's decode64 fail as long as you give it a string since it does not.
I am not sure what you are looking for, but ruby has strict_decode64 which does fails on invalid characters and invalid encodings, and urlsafe_decode64, which relies on that too. I think they've been there for a few years.
I’m not looking for anything, just pointing out that defaults are insanely variable, and people use them because they are the default. If you don’t know and don’t care and need to generate base64 you’ll use decode64 because that’s the default and it worked fine.
And if I have to interact with that, it’s an interop issue
And if I have to interact with that, it’s an interop issue
It does seem unfortunate that what's apparently (given its name) the 'default' base64 crate for Rust can't expose a simpler interface. Most people don't need optimally efficient base64 encoding. It should suffice to expose 'encode' and 'decode' functions that take some parameters configuring the encoding. That's not to say that the more complex design of this library is necessarily bad, just that it seems to be motivated by goals that won't be relevant to most people who just want to encode a smallish blob.
Maybe this illustrates why it's good to have base64 in a language's stdlib. That way 90% of people can just use a couple of simple functions, while libraries like this can still exist for those who really need them.
Maybe this illustrates why it's good to have base64 in a language's stdlib. That way 90% of people can just use a couple of simple functions, while libraries like this can still exist for those who really need them.
It’s not about efficiency. It’s about interoperability. There are a gazillion minor variants of Base64. Being explicit about which one you’re using is entirely sensible.
Yes, but you can achieve that just by having a normal function that takes some configuration parameters. The documentation of the library suggests that efficiency was a significant concern, and I assume that’s why the implementation is not as straightforward as it otherwise might be.
> This has already been discussed extensively elsewhere. I am not sympathetic to concerns over useing a trait. Rust is not the language for you if you don't like traits. Feel free to collect a full refund on your way out.
Without weighing in on the merits of the comment, the delivery feels very Poettering-esque.
Without weighing in on the merits of the comment, the delivery feels very Poettering-esque.
yes and it has little to do with liking or dislinking traits tbh. having to import a trait to use what boils down to a single pure function is never good design
BUT the convenience methods defaulting to "standard" base64 instead of "url safe" base64 because it's one of the things where people don't think about there being more then one standard/configuration and it then loading to bugs in their code
so deprecating it makes sense, and without a braking change (which makes no sense here) you can't replace it with anything better
and because of the prelude module the import isn't that bad e.g.: `use base64::prelude::{Engine as _, BASE64_STANDARD};`
and even ignoring that most imports are auto generated, sorted and formatted by rust analyzer for most people most of the time
so it's eitherway at most a minor anoyence
so I can understand that the author is feed up with people annoying him about that again and again and again
BUT the convenience methods defaulting to "standard" base64 instead of "url safe" base64 because it's one of the things where people don't think about there being more then one standard/configuration and it then loading to bugs in their code
so deprecating it makes sense, and without a braking change (which makes no sense here) you can't replace it with anything better
and because of the prelude module the import isn't that bad e.g.: `use base64::prelude::{Engine as _, BASE64_STANDARD};`
and even ignoring that most imports are auto generated, sorted and formatted by rust analyzer for most people most of the time
so it's eitherway at most a minor anoyence
so I can understand that the author is feed up with people annoying him about that again and again and again
I know it's not entirely fair, but seeing stuff like this makes me wary of the Rust community.
I have bad news for you about <any language> community…
People behaving poorly on github issues/PR discussion is so common in every language it tells much more about the internet/open source culture than it tells about Rust users (there's no such thing as a “Rust community” nowadays BTW, it's not 2016 anymore and the language user base has grown so much the user base is now as fragmented as it is for any mainstream language).
People behaving poorly on github issues/PR discussion is so common in every language it tells much more about the internet/open source culture than it tells about Rust users (there's no such thing as a “Rust community” nowadays BTW, it's not 2016 anymore and the language user base has grown so much the user base is now as fragmented as it is for any mainstream language).
Yeah, it's not fair because there's no way this person represents the Rust "language community" it's just a couple people arguing on a bug ticket.
And in fact there's others who would make a counter argument that excessive reliance on traits is a bad over-complexity-smell.
The reality is it's fairly large language community already, and lots of people have lots of different opinions.
And in fact there's others who would make a counter argument that excessive reliance on traits is a bad over-complexity-smell.
The reality is it's fairly large language community already, and lots of people have lots of different opinions.
Every language will have people who make extreme statements of one kind or another. I wouldn't call that representative of the community in general.
Pretty sure the maintainer is also a Discord mod.
new language...same old dickheads...why are these people even contributing anything? they obviously dont care about the users
It’s free software. There is really no obligation that they need to discuss closed issues.
If it matters so much create one of your own. Why have a go at open source maintainers doing things on their own time?
If it matters so much create one of your own. Why have a go at open source maintainers doing things on their own time?
Entitled mindset that equates "open source" with "free stuff to make my day-job easier."
superkuh(2)
I really wish people would drop the "explicit is better than implicit" mantra.
What "explicit" means vague. Does it mean you want the code to be verbose? Noisy? Because that's silly, and Rust doesn't even have a history of doing that. We went from try! to ?, we went from impl Future to async/await. I'd rather people talk about what they really mean than just repeating that line over and over again.
edit: also it's really weird the OP seems to keep wanting to post this thread. If you don't like their library, fork your own?
What "explicit" means vague. Does it mean you want the code to be verbose? Noisy? Because that's silly, and Rust doesn't even have a history of doing that. We went from try! to ?, we went from impl Future to async/await. I'd rather people talk about what they really mean than just repeating that line over and over again.
edit: also it's really weird the OP seems to keep wanting to post this thread. If you don't like their library, fork your own?
I personally would like to have the raw interface or both the raw and simple interface available. Some package maintainer (in whatever language) would assume everyone has the same requirement as them and make only the 'ultra simplified interface' available. And it's generally a big headache if it didn't met your requirement. So you end up copy paste and modify or forking the whole project.
But that could totally be avoided if you just export both of them instead of hard-locked the interface. People don't really want to fork the god-damn project just to change the two character you hard-coded.
Even marking them unstable and making it unsupported would be far better than locking it down completely.
But that could totally be avoided if you just export both of them instead of hard-locked the interface. People don't really want to fork the god-damn project just to change the two character you hard-coded.
Even marking them unstable and making it unsupported would be far better than locking it down completely.