#Rust2019 blog post: The Last Thing Rust Needs

Yes, I think so: Pre-RFC: first-class support for compile-to-Rust languages

5 Likes

Macros 2.0 is needed before this can reasonably be said, but has been going with no RFCs for ages, and needs help. (Existing macros have a ton of problems, like how break is unhygienic.)

5 Likes

Autocompletion seems like a good solution that is underutilised. Using some nifty AI neural nets, it should be possible to find common patterns across code bases and apply them predictively. Is anyone aware of such a thing being used? Since neural net AI is relatively new, it seems futuristic and awesome to have an AI co-programmer integrated in the Rust language (probably part of an IDE) to 'code as you think', meaning its predictions are so good that you can skip more than half of the typing.

2 Likes

Rust barely has basic IDE integration, and in some areas is still playing catch up to C++ and Java IDEs from 10 years ago. I don’t think it’s realistic to hope that in the short term we’ll have someone solve Rust’s IDE shortcomings just by adding some AI to it.

The chores that Rust has, e.g. management of use statements, lifetime annotations, or delegation of traits, are rather boring from AI perspective. They have clear logical answers that can be computed with classical algorithms or even solved by outputting predefined code templates.

17 Likes

A nice place to use AI in IDE is for prioritizing completion variants. However, most of other problems have precise solutions: you don’t need AI to generate an impl from a trait, or to replace a match with an if-let, or to do a project-wide refactoring.

13 Likes

Great post, and I absolutely agree. IMO we have something of a problem community-wise in Rust in that we’re perhaps more than a little bit inclined to overrate a variety of things that are certainly commendable but at the same time in no way groundbreaking.

For example, Rust’s cross-platform support, while quite good, is nothing to write home about. There are other languages that aren’t C, don’t run in a virtual machine, and are not garbage collected out there that can run in and target quite a few more places while not even using LLVM at all.

KillerCup recently asked in their “Rust 2019” blog post “Did you think about the emergence of an idiomatic GUI library?” and the answer is yes, I have, many times!

Unfortunately, as I think this thread on the Rust user forums as well as this one here on Rust Internals shows, there are a lot of people in the community who have what is in my view a perspective of “nobody has ever done [some thing] specifically in [C++] and this means that it is therefore a dead-end not ever worth considering.”

(The “some thing” being in the contexts of the two threads I linked, the mere idea of an universal GUI abstraction that works the same way everywhere.)

To be blunt that kind of thinking is just a HUGE mistake with an actively detrimental effect on various surrounding things, IMO.

Why do we care at all about what anyone has not done in C++ (or other languages depending on the topic) previously? Why can’t we start thinking on a more broad scale and start just doing stuff for no reason other than we think it is worth doing?

8 Likes

I like the education suggestion, and I like the idea of 'seeing to' the queue of things. However, I found myself pausing at a few places in this blog post, and I think it comes back to this bit:

A noble goal — however, during the last year or so, I increasingly felt that this goal is being used as an excuse to try and cater everyone. Rust is general-purpose, but it's still fundamentally a systems programming language at heart. Unfortunately, some Rustaceans interpret "general-purpose" as "must be perfect for anyone and everyone." I don't think that is a good or even viable approach to language design.

In particular "systems programming language at heart". Rust is a language capable of doing 'systems' programming [0], but it's also capable of doing more than that (e.g. crater could be written in Java), as you note when you call it "general-purpose".

But I'm not sure how you're determining what Rust is "at heart". I would understand something expressed from a particular perspective - e.g. with my OS hat on, I might make my biases clear and say "I personally am only interested in the features that lend themselves to OS implementation, and I think fixing the asm! macro is far more important than extra type system things". This provides the context to see the shape of the language you're envisaging when proposing a limitation of language scope, rather than needing to guess. E.g. I find pointers awful to use in FFI code - this typically falls under "systems programming", but it means new syntax - I have no idea of your opinion because I don't have much to go on.

Fortunately, there is a Rust team whose responsibility it is to think hard about the Rust language, both right now and in the future - they need to take in the different shapes of Rust that people envision, and try and synthesise them into a coherent whole. I may disagree with some decisions (which may regress maintanability/teachability/consistency for a 'shape' I care about in favor of something I oppose), but I do trust the team and their steering to improve Rust overall. I realise it's hyperbole, but I don't think anyone is even close to pursuing "perfect for anyone and everyone" and I've seen 'negative scoping' on RFCs in the past.

On the subject of language design the post goes on to say:

Stuffing the language with more and more features just to ensure that more people will like it is marketing or even compulsion for conformity, and not a professional way of solving a technical problem.

and you mention in your suggestions:

Thinking about the bigger context. Not sacrificing the long-term maintanability, teachability, and consistency of the language for short-term goals.

I feel like the first quote goes without saying, but the second quote suggests you think there's something to improve on? I don't have the impression that the language team takes these matters lightly so I'm wondering if there's a concrete suggestion (otherwise it sounds like business as usual!). That's not to say things can't improve - for me, I wonder if being more explicit about language shapes (OS dev, beginner friendly, ...) would help understand rationale behind RFCs. I'd have to think more about how that would work.

[0] there are a few definitions of systems programming, but let's say it roughly means "you can build an OS, and you can manage your own memory"

9 Likes

As far as I can tell the biggest problem is that the current most-advanced tool for Rust that relates to IDEs (RLS) relies directly on output from the compiler, which quite frankly may never be anywhere close to fast enough for that to be a particularly good idea. (Note I mean no offense by this, but, it just seems… extremely clear to me that that is the case, currently.)

I don’t think anything other than something that parses Rust by itself and is in no way reliant on rustc, and can perhaps even be statically linked and used in-memory / in-process by IDEs will ever be truly viable as a long-term solution.

6 Likes

My point was that some of the goals people are trying to set for Rust are out of context for its original direction. I’m not trying to litigate the definition of “systems programming”; maybe it’s not the best word I could use.

But I cited a specific example: Ok-wrapping and throws. I get that it might be convenient for people coming from different languages or for beginners. But it would make Rust lose some of its elegance and simplicity.

So, in this case, convenience and language orthogonality might be in tension, and I argue that the latter should be preferred, because:

  1. the issue of (initial) inconvenience can be easily resolved by educating people and getting them comfortable with Rust style and idioms. From what I can tell, most of the time, complaints about perceived inconvenience simply mean “Rust does it differently, and I’m not used to that in C++/Java/Go/C#/Ruby/JavaScript”. I didn’t mean to bash T-lang here; it was meant to be much more of a message to newcomers and to the community in general. To put it simply and roughly, those who want to program in Rust should make efforts to learn Rust, and shouldn’t try to “program in a different language” using the Rust compiler, so to speak. I think this is pretty standard in the practice of basically any mainstream language used today.
  2. there are already other languages with the tradeoff resolved the other way around, and they are specifically designed to accomodate even marginal gains in ease of use at the expense of resulting in a less general, less elegant, or less robust overall programming style.

There are plenty of environments and languages for getting started in programming, there is Processing, various JavaScript frameworks, etc. that focus specifically on beginner-friendliness. But you aren’t going to be able to build large-scale software systems using these technologies. This is where I think Rust should draw a clear line and not sacrifice important baseline values, such as orthogonality of features, which in turn have a significant impact on its future evolution as well.

8 Likes

I would say they usually are in tension.

Rust has a ton of such convenience things; removing them is a big part of why MIR exists. Even things like if and while are just unnecessary, non-orthogonal convenience features.

So, as usual, I'd be more interested in seeing more nuanced heuristics about when such things are worth adding than a blanket "we don't need convenience" statement.

11 Likes

I’d also add that, depending on your perspective and which incarnation of it you’re talking about, Ok-wrapping is more elegant and orthogonal than leaving it out.

So at best this is a bad example, but at worst your whole point is an unfortunate oversimplification that ignores a lot of important factors. The same arguments apply to other features you’ve argued against in the past.

3 Likes

And I'd like to see a more nuanced reading of my actual statement – it wasn't "we don't need convenience". We do, the point is that they come with trade-offs which are not always apparent but should probably be taken more seriously.

Depending on your perspective, you might disagree, but it doesn't make it a bad example. It's a perfectly fine example of a problem that exists, and it demonstrates my point.

It is also kind of unfair to attack my argument based on the idea that "if this feature were different, it would be more orthogonal and elegant". Sure, basically any feature name could be arbitrarily reinterpreted to mean anything else, but there's not much point in doing so. I'm feeling you are focusing too much on words here. Whether or not it's called Ok-wrapping or something else is not my point; what I was criticizing in that specific example is the mechanism and method of special-casing yet another concrete type with the (primary or only) goal of removing a couple of characters to type. There has been such a proposal in the past; it might have been iterated, modified, renamed, or even withdrawn since I read about it last time, but my point (about its specific behavior at the point of time when I saw it) still stands.

2 Likes

But that's not what Ok-wrapping in the current implementation of try { .. } does; it also Some-wraps or Poll<T>-wraps.

There have been many different Ok-wrapping proposals; some more elegant, some less, some more special cased, some not at all. Concretion would help advance this discussion.

2 Likes

Using for instead of explicit loop { .. } is also can be seen as a "special-casing yet another concrete type with the (primary or only) goal of removing a couple of characters to type". And do not forget that many Ok-wrapping proposals were defined in terms of Try trait, so you will be able to use this functionality for any custom type.

As a proponent of Ok-wrapping I believe that things like Ok(()), return Err(MyError) or Err(MyError)? are too common to deserve a special attention. Also I believe this feature is quite orthogonal to other language features and does not introduce any incoherences.

UPD: To be explicit I have in mind the following flavour of try fn/Ok-wrapping:

// Note that we still have to use `Result<(), MyError>` in signatures
try fn foo() -> Result<(), MyError> {
    if flag() { throw MyError }
    // no need for Ok(())
}
try fn bar() -> Result<u32, MyError> {
    foo()?;
    1
}

Excellent, then the relevant part of my criticism doesn't apply to the current implementation.

1 Like

I don't see how you can possibly make this claim while we're simultaneously seeing blog posts about how RFCs get too much feedback. The trade-offs have high visibility and are taken very seriously.

Here's a question for you- what would it take for you to see them as being taken "seriously enough?" Immediate termination of discussion around all features you dislike?

This is absurd and not what anyone is doing. I'm talking about the primary, accepted form of Ok-wrapping, as explicitly opposed to the unfiltered, assorted musings found on i.r-l.o. I'm talking about the form that everyone calls "Ok-wrapping," and that you yourself have discussed in the past. That is the version that I'm calling orthogonal and elegant.

2 Likes

Sure, but think beyond that even. What about typing the program logic and the data structure get's auto-inferred to a large extent? What if the refactoring can be automated to a large degree using similarities between other refactorings? The design space is massively underutilised imo, even after adding all the 'simple' autocompletion functionality.

I haven't been following the Ok-wrapping conversations too closely personally. I certainly do not, off hand, know the differences between what the "accepted" form of Ok-wrapping is and what the assorted musings are.

Folks here might consider that not everyone is on the same page with respect to Ok-wrapping. As an example, from my perspective, when I hear, "Ok-wrapping," I hear, "change the language semantics to implicitly insert Ok around values in certain circumstances." I wouldn't be surprised if 1) others perceived it the same way and 2) in that light, others might react negatively to the change. (I know I do.) I just did some searching, and I can't seem to find something written down that describes what the "accepted" version of Ok-wrapping is. (I suspect I am not using the right keywords.) For example, I don't see an RFC anywhere about it? I hope I didn't miss it, because I'm certainly very interested in that addition and would like the opportunity to give feedback.

I'm not trying to get between you and @H2CO3 here, but just thought I'd share my perspective on what "Ok-wrapping" means to me.

12 Likes

The only accepted form of Ok-wrapping (to my knowledge) is what was originally accepted as part of the ? + catch block RFC: RFC243 § catch expressions. Since then "catch expressions" have been renamed to "try blocks" in RFC2388.

This is a very limited form of Ok-wrapping, only the implicit return value from a try block is wrapped into the success variant of the block's return type.


While I do agree with some of the sentiment of the blog post, this example specifically I feel does reduce its impact somewhat. Especially this specific quote really annoyed me while reading it:

However, after a short period of getting used to, the (minor) pain associated with having to write an explicit Ok or return Err(...) quickly fades away.

I have been writing Rust as a hobby (and a very small bit of work) language for the last 4 years, and still every single time I have to write Ok(()) at the end of a small closure that does some IO I feel the readability pain. Maybe the pain has been numbed slightly by repeated infliction, but I really wouldn't say it's faded away.

9 Likes

That second RFC says

// Note: OK-wrapping is assumed here, but it is not the goal of this RFC
// to decide either in favor or against OK-wrapping.

which seems a bit tenuous to me, I dunno.