Rust 2020: Growth

As an experienced Rust user, I can tell you it doesn't get any better once you do understand said features. During the last year (or so), several features have been rushed to stabilization, despite widespread controversy (at best) or almost unequivocal opposition (at worst) on the part of the users of the language. The semantics of these doesn't particularly puzzle me, but they do tend to go increasingly against the principles of simplicity, understandability, orthogonality, learnability, and elegance, which I find worrisome.

At this point I'm not sure what could convince the governing forces of the language (lang/core/etc. teams), if a repeated, emphasized, explicit dissent of the wider community didn't.

4 Likes

I too am worried for much the same reasons.

There is a reason C rules the roost in the embedded world. There is a reason operating systems like Unix, Linux, Windows are written in C. The ultimate and still today the only systems programming language. Despite its short comings.

My hat is off to the C standards committee guys for not corrupting C with all kind of new features over the decades. C is simple. It does what it does and that is that. They leave the ever growing and insane complexity of new language features to C++.

So what about Rust?

Rust touts itself a "systems programming language". To my mind this is only true if:

a) It does not require a run time environment.

b) It is actually useful without a standard library.

c) It just generates executable code. That I can run how I like. Like C.

On the other hand... I love the idea of a type safe, memory safe language that can be used as easily as Python or Javascript to do all the things we do in Python or Javascript.

Looks like a difficult tight rope to walk. Who knows what will happen.

1 Like

I don't think that criticism is justified, though. These new features don't fundamentally (or at all) improve type and memory safety. Rust was already type and memory safe by design from, say, version 1.0.

Said features are usually touted for "improving ergonomics", for some (often arbitrary and/or imaginary) metric of "ergonomics". They don't prevent more uses-after-free than the ownership system, they don't prevent more race conditions that the Send and Sync auto-traits. They usually consist of either syntactic sugar for a slightly more complex way of doing something, or make Rust look more like <!-- insert favorite language here -->. Neither of these is something that needs to exist in order for Rust to fulfil its promise.

3 Likes

I did not really want to be critical of Rust there. It is after all the only new language I have learned since 1979 that has a genuinely new and useful feature, memory safety, for a compile to native instructions language. That is the reason I am here at all.

I can't comment on the "ergonomics" thing much. To some that means making it look like Haskell. To others it means making it look like Java or C#.

Me, I want a systems programming language.

I am working with embedded firmware, and would love to use Rust. However i have mentioned it socially a few times and the main feedback i get is that we need:

  • Stable, standardized and documented ABI (so it is possible to link compiled closed source libaries and avoid requiring explicit compiler versions).
  • Multiple independent implementations, so the language as a clear specification that is independent of the compiler implementation.
  • Possibility to get a safety certified compiler (i.e. Ferrocene)

Basically it is hard to beat 40+ years of stabilization from C. However i guess we are a bit conservative, just recently moving from C89 to C99.

When it comes to language features, i am pleasantly suprised what is possible, even though some things are a bit hard to figure out how to do:

4 Likes

I was attracted to Rust by his promise of speed and rubustness.
I liked the capabilities of external Command Calls with Result Capture.

But the most off-putting issue is that it is extremely hard to learn.

What makes so extremely hard to learn.
Examples:

  • Rust knows 5 different types of Strings and each Function requires a different Type of String, but to figure out how to get from one String Type to the required one is extremely hard by its scarce documentation.

     pub fn parse_u8(text: &[u8]) -> String {
    
      for uc in text.chunks(1) {
          if (uc[0] >= 32 as u8
              && uc[0] < 127 as u8) {
          }
        }
     }
    

In this example text is a Reference to a Vector of Bytes but I can't compare them to Numbers. First I need to figure out that I need to use as u8 to cast them into Bytes. But there isn't any documentation about it.

  • Rust does not allow NULL Pointer. But there isn't any documentation on how to solve the most common Programming Tasks that in other Language are solved with NULL Pointers.
  • Rust has many very language specific features that are completely new to Programmers coming from other languages. For introducing completely new Concepts of Programming (Result, Option) their documentation and mostly their application in day to day work is not clear and needs to get used to it. Also it complicates the code much more than in other languages which results in code much harder to understand and to maintain.
    All the Places Patterns Can Be Used - The Rust Programming Language

we can’t combine these two conditions into if let Ok(age) = age && age > 30 .

I like to add that the interesting thing about this example is that age != age because age outside the Block is a Result while age inside the Block is the Content of Result. Because after the Block you need to treat age still as a Result which can contain an Error.

  • Rust by Example Documentation: Most examples are too complicated for new Programmers to understand and thus to apply to Real World use cases. They rather want to SHOW OFF the capabilities of the Author to concadenate Function Calls. For being such complicated Examples their explanation is just too bad.

Suggestion:
Rather on new Language Features that are not used by new Programmers focus on better Documentation
Many languages try to help the Migration from other Languages by publishing Sections aimed to help for this purpose "Pitfalls for Programmers coming from other Languages".

2 Likes

Most of this tasks are solvable with Options (the only exception I can think of for now is FFI), and its connection with nulls is described in The Book. I'm not sure what could be much better here.

Doesn't this worth an issue with explicitly stated unclear cases?

Taking some examples from the Forum

fn split_text<T: FromStr>(s: &str) -> impl Iterator<Item = Result<T, T::Err>> + '_ {
    s.trim_start()
        .lines()
        .take_while(|line| !line.trim().is_empty())
        .map(|x| x.trim().parse())
}

That is exactly the kind of Code that People "loved" soo much about Perl.

Getting help like this raises even more questions that it solves.
And not knowing exactly what each Function Call does makes any adaption to the real use cases even harder than solving it.

But work is being done that does improve things that are more than sugar. MaybeUninit was a big one. const fn is a work in progress still but it fills a very useful space. NLL were a consequence of improving Rust's internals. Work continues on const generics but I'm hoping for a 2020 release (mind you, last year I was hoping for a 2019 release).

I'm not going to claim every new feature is like the above but not everything is async/await.

1 Like

I think @H2CO3 is referring to features like match ergonomics or impl Trait in argument position, which were subjected to some controversy that he feels was insufficiently acknowledged by the lang team.

2 Likes

Ah ok, I got confused between arguments it seems.

I have come to believe there is some pre-processing going on in front end of my visual cortex that filters out random noise.

Confronted with code like that said pre-processing can't recognize it as any kind of communication between intelligent beings and discards it as potentially distracting line noise.

1 Like

short resume: help like this does Rust hinder from being widely accepted by Programmers.

As an honest Answer to the Initial Question:

These are nothing like what I'm talking about, though:

  • MaybeUninit is a library type (that removes many common patterns of misuse of uninit'd memory)
  • const fn and const generics were/is a significant part that is 1. pretty much necessary for efficient low-level abstraction and 2. allows a ton of other very useful solutions to real-life problems that would be impossible otherwise, and proven-good analogues to it exist in many languages, 3. it was severely lacking in Rust until recently. IOW the complexity it adds to the language is worth it.
  • NLL doesn't affect the language surface at all, it's merely making the borrow checker smarter, allowing more logically correct code to be accepted, which was erroneously rejected in the past.

Things that are being discussed or have been accepted/stabilized are: pattern match ergonomics, postfix await, implicit format arguments, property syntax, various forms of delegation, inheritance proposals, adding more and more operators for control flow, allowing implicit conversions. These are (some of) the particularly worrisome ones because many of them (eg. implicit conversions and operator creep) have been demonstrated to be objectively and overwhelmingly bad by decades of experience, yet they are not being unequivocally rejected from Rust. Meanwhile others are "just" annoying and/or unnecessary, or have the same high potential for abuse (especially in the context of unsafe code).

5 Likes

I respectfully disagree that it's "extremely hard to learn". That, at best, is subjective, but it also depends on one's background. It had basically no learning curve from me. I don't know what the "5 string types" you are referring to are, but different string types are there for a reason, and it's almost always the same reason: ownership vs. borrowing.

You don't have to memorize all the APIs and string types like that, that is the wrong approach. Why a particular type is used in a particular situation has a logical explanation behind it. Seek to understand the reasons instead, there is far fewer of them than concrete types in the standard library.

I'd argue that the fact that other languages didn't make you reason about ownership, allocations, etc. is not the fault of the hard nature of Rust; instead, it's the sloppiness of the other languages.

7 Likes

Now you really have me worried.

More operators, implicit conversions and property syntax sound positively frighting. A lot more illegible "line noise".

I was already disturbed enough by the prospect of "parity with C".

But it's inevitable. As more users come, all with their own experience, preferences and application domains, more suggestions for "just one more little feature" will come with them. After all feature X worked so well in language Y, Rust really needs to have it.

Before you know it Rust is mangled into a dogs dinner. An unintelligible chaos like C++.

I have been investing my time in Rust in order to get away from that. It would be a shame to find I'm back where I started!

In general, having 10 ways to do the same thing, like Perl, is really bad for anyone having to read the code later. One is good enough.

2 Likes

Agreed. And that's why my motto is generally: "Do not try to cater everyone". Rust should not be a language that aims to please each and every developer coming from each and every already-existing language. Prior art is nice, but should not be taken lightly. And I think pleasing everyone is simply neither realistic, nor possible. It's not a meaningful goal, either.

Unfortunately, this seems to be a minority opinion, because – in my perception – people confuse "beginner-friendliness" and "learnability" with "similarity to other languages". The latter can be good, but not always and certainly not in the context of every feature ever.

This, by the way, is another common fallacy. Ideally, a programming language needs to exhibit consistency and coherence. Thus, features should interact clearly and logically, and they need to fit into the "big picture", i.e. into the global context of the language, aligning with its primary goals.

As a consequence, saying that "this feature is good in language X, therefore we should also have it in Rust" is extremely misguided, because it might be a fine or beneficial feature in the context of all the other features, idioms, and approach of the other language, but it might actually be detrimental to a completely different language like Rust, simply because it doesn't fit in.

6 Likes

About Strings and why they are hard to learn:
You got:

  • String and str
  • CString and CStr
  • OsString and OsStr
  • Vec<u8>

Then the relationships between then:

  • Command.output() gives you a Vec<u8>
  • std::env::ArgsOs gives you a OsString
    but all String Operations are defined for str.
  • str you can pass as reference by &str but String you can't pass as &String you must use String.as_ref() or if a &str is required as String.as_str()
  • String::from_utf8() does not create a String it creates an Result object.

All this stuff might seem trivial to Programmers who are already used to it but they are a huge road block for New Programmers which make a trivial console application an endless struggle.

So the Point is:
For having so many different String Types the documentation on how to work with them efficiently is too poor.

You can discard this advice as it is only me but I have already read too many posts on the forum about people struggling with the same issues.

4 Likes

OK so let's see this one-by-one.

  • Vec<u8> is not a string type.
  • Usually, when inside a Rust program's components, String and str are the primary types to be used. Conversion between them is trivial (AsRef/Deref and ToOwned/Into).
  • CStr and OsStr usually come from APIs that you use to interact with the OS, processes, network streams, etc. You specifically mentioned Command and env::ArgsOs. This is a necessity because Rust's String and str types are defined to be UTF-8, and not every operating system respects this.

To address the various complaints you made:

  • std::env::ArgsOs gives you a OsString but all String Operations are defined for str. – You can conditionally convert an OsString or an OsStr to a &str if the former are valid UTF-8.
  • String::from_utf8() does not create a String it creates an Result object. – Yes, but it's a Result<String, FromUtf8Error>. I.e. if it does succeed, it gives back an Ok(String) to you. If this is not clear, then you need to read more about Rust's error handling, this is not the fault of the String type. All fallible conversions in Rust work similarly to this.
  • but String you can't pass as &String you must use String.as_ref() – this is factually untrue, it is possible to pass String by reference (as it is possible to pass all types by reference). Furthermore, due to the feature known as "deref coercions", it's even possible to pass a &String when a &str is expected, because String implements Deref<Target=str>.

All in all, it might be beneficial to add more examples (for instance) to the relevant section of the Rust book in order to demonstrate working with strings in various situations. However, for the most part, it seems to me that your annoyance comes mainly from not having yet much experience with the language.

I can't blame that on any language though: I don't think it's a realistic goal to master a language without investing a lot of effort and time into learning it. I would assume that is (or should be) completely non-controversial.

5 Likes

I think by explaining the details you're missing the point here. Most other languages have one universal string type that does everything, pretends character encodings don't exist, and sometimes even stores arbitrary binary data. And even in C, which theoretically needs to deal with similar level of detail around strings, the ownership and byte/char distinction doesn't exist at the language level, so C programmers aren't used to thinking about them explicitly.

IMHO Rust handles strings the right way, being very explicit about costs and complexity that are involved. But it is a shock for anyone who's new to this. And it's fair to ask for this to be explained better to newcomers who never dealt with ownership.

25 Likes