Standard library vs crates

Mistakes in the standard library can potentially be fixed by an Edition. Indeed, in the latest 2021 Edition we had " * The TryInto , TryFrom and FromIterator traits are now part of the prelude."

However due to the human retraining cost, I think the bar is rather high for any changes that are not 100% backward compatible, and I doubt we will see significant changes of that nature in practice, even for items that are long deprecated.

[ I just went looking for an example and found abs_sub ]

Generally, the standard library itself must be identical regardless of edition, so things like the signature of a function can't be fixed by an edition.

2 Likes

Isn't it possible in principle for the edition upgrading program, cargo fix I think it's called, to edit the source code to upgrade it? As described here.

You can still compile a crate on edition 2015 today, and compiling something that has components with different editions is explicitly supported. That is, the guarantee has been made to keep compiling code that has not been upgraded and "fixed".

From the latest RFC on the topic:

Editions do not split the ecosystem

The most important rule for editions is that crates in one edition can interoperate seamlessly with crates compiled in other editions. This ensures that the decision to migrate to a newer edition is a "private one" that the crate can make without affecting others, apart from the fact that it affects the version of rustc that is required, akin to making use of any new feature.

The requirement for crate interoperability implies some limits on the kinds of changes that we can make in an edition. In general, changes that occur in an edition tend to be "skin deep". All Rust code, regardless of edition, is ultimately compiled to the same internal representation within the compiler.

1 Like

I can imagine a system where you could change the signature of a function in the standard library, and somehow when it links an old edition crate with the new standard library it knows to select the old function instead of the new one. But the whole idea seems abhorrent at the human level, and cargo fix cannot fix everything ( in particular macros cannot always be fixed if they are calling a function in the standard library that has changed ). So it's basically a non-starter.

Their are a lot of additional barriers like taking function pointers and functions having their own distinct types, but if we're agreed it's a non-starter, there's limited benefit to discussing them.

2 Likes

I would like to throw in an example regarding this:

I'm not sure if it's the same what I tried to describe, but let's take a look at tokio::io::AsyncWrite and tokio::io::AsyncWriteExt. When we have a function

fn foo<W: AsyncWrite + Unpin>(writer: W) {
    use tokio::io::AsyncWriteExt;
    /* … */
}

then it should be easily possible to later change that function to:

fn foo<W: AsyncWrite + Unpin>(writer: W) {
    use tokio::io::AsyncWriteExtVersion2;
    /* … */
}

And any caller would still see the same interface. Or am I wrong here?

I'm curious what has been the original motivation for placing the "non-core" methods into an extension trait in case of Tokio. I don't think this is for replacing the extension trait in later versions, or is it? Maybe it has to do with issues regarding async and default implementations in traits? Perhaps @alice, you can tell?

Well you are correct that what you suggested would not change the interface of the function. However I think it's just a pattern that has emerged in the async world β€” e.g. Tokio has to use this pattern for things like Stream, because Tokio doesn't define the Stream trait, so Tokio cannot add methods to it. The IO traits use the same pattern for consistency.

The RFC to add Stream to the standard library did include the next method right there in the main trait.

1 Like

I usually try to avoid the pattern in my code unless I'm forced to use it (I preferred default implementations for syntactic reasons until now), but I find it interesting that the pattern allows for future method changes without breaking API compatibility – (at least in theory, not sure if it's really feasible/advisable in all most cases).

Of course, that won't help in case of std, and I given the current syntax/structure of Rust, I think it's nearby to use default implementations instead of extension traits in many cases. But maybe this is a door opener for improving agility in future (perhaps hand in hand with some extra support by the language).

Just to note: I do see a next method mentioned here in RFC 2996, but currently the documentation for std::stream::Stream does not list next in the list of provided methods.

Since a "Rust Platform" (a list of officially endorsed/recommended non-std crates) has been effectively re-proposed multiple times in this thread, we should really link the old threads about that and why it ended up not happening:

(I'm not in a position to judge whether the situation has changed enough since 2016 to make retrying this a good idea)

2 Likes

I think the entire idea of "a list of officially endorsed/recommended non- std crates)" is something of a contradiction in terms. Things that are by some criteria "official" are in std. Everything else is not.

Who should these "officials" be? By what criteria should they endorse or recommend anything?

I liken it to my recent dilemma in choosing some new kitchen appliances, oven, cooker, refrigerator, etc, for a kitchen renovation. In this modern world there are hundreds of makes and models to choose from. How could anyone be sure what would be the best deal or best fit for their purpose or most reliable or... etc, etc. One can read endless reviews and comparisons and YouTube videos and just get more and more bewildered.

In the end, facing unmanageable amounts of information, one has to make a choice. The decision comes down to things like brand loyalty, my family has always used brand X so will I, or getting what a neighbour has because that seems to work well, or don't like the colours.

In the same way, people will select crates. Choice is good, right? Quality, generally useful, crates will become commonly known. Recommendations will be made. For those that don't want to spend a lifetime evaluating everything they might use.

1 Like

Thanks for sharing these links.

I mostly wanted to say that as a newcomer to Rust, it feels a bit overwhelming to choose the right 3rd party crate for a standard task (such as scoped threads). Maybe that problem will be fixed automatically (both because of my increasing experience, and because some solutions might evolve as de-facto-standards, until some better solution comes along).

I also mentioned the Rust Nursery. Such projects or websites/projects categorizing existing crates could be done by anyone, and it doesn't need to be the Rust team to work on something like that.

I felt the same way at the beginning a year or so ago. Still do to some extent.

For example what web framework to use, Actix or Rocket or other. I was not about to study all the code of all the options to see what is good for me. No, I read around, watch some vids on Youtube, get a feel for who is developing the thing and how I feel about them and their goals.

Then there have been recommendations made by people whose opinions I have come to respect on this forum.

Keep you ear to the ground, your finger on the pulse. As it were.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.