Rust's 2018 roadmap
blog.rust-lang.org
blog.rust-lang.org
As someone not only new to Rust but also systems programming in general I especially appreciate that the chapters include actual reasoning about why things are how they are in Rust and that they seem pretty open about drawbacks of the choices. As well as the use of precise language instead of trying to be too cute and overly beginner friendly.
An example for this is the lengthy page on Strings: https://doc.rust-lang.org/book/second-edition/ch08-02-string...
It really makes me feel you want me to understand what I'm doing from the start instead of getting a generic app out of the door as quick as possible and then tediously figure out the next basic steps from screencasts, scattered blog posts etc.
Also big plus for having the book and api doc available offline by default!
I'm going to try to come by #rustdoc and see if there something I could contribute.
This trait of documentation (the "why" & openly discussing pros and cons) seems to be a property of great systems being well-documented.
I would contrast this to e.g. the Erlang documentation. Erlang/OTP is a great system, and it's well-documented... but the documentation doesn't actually go into the "why" of various choices (e.g. why the BEAM has a module table with exactly two slots per key; why binaries are shared at ≥64 bytes; why clusters are fully connected; etc.)
But I think this makes sense, given the differing approach: unlike Rust, the Erlang/OTP platform is trying to facilitate an abstraction where you just write high-level declarative-ish code, and then improvements to the platform will make that code perform better over time.
Rust documents its decisions because it expects you to be someone who wants to know those things in order to make precisely those decisions (except you're choosing a systems language, rather than designing one.) Erlang leaves most of its internal implementation as a black box, because it expects you to treat it as a black box and derive advantages (in e.g. maintainability) from doing so.
We don't assume that. From https://doc.rust-lang.org/book/second-edition/
> This book is written for a reader who already knows how to program in at least one programming language. After reading this book, you should be comfortable writing Rust programs. We’ll be learning Rust through small, focused examples that build on each other to demonstrate how to use various features of Rust as well as how they work behind the scenes.
That is, we assume programming knowledge, but not specific programming knowledge. Many people come to Rust not knowing C or C++.
As a post-structuralist, I generally approve of reading the author's stated intentions and responding, "Yes, well, that's one interpretation." :P
Growing up, I was constantly taught new things, without often being taught why. They "why" of things is just as important to me as the "how".
So thanks for all you've done and continue to do!
I rather meant that the goal of someone learning Rust "in anger" is for that person to quickly decide if Rust is the best language to use to solve the problem they have. And, if Rust is even a candidate in their solution-space, then usually C and C++ (and sometimes also Go or D or C#) are the other candidates the learner is considering learning. They're Rust's "neighbours" in its solution-space.
And so, Rust's documentation is well-written, but it's well-written specifically for this type of person learning Rust "in anger", with the goal of evaluating the language against its neighbours at the same time they're learning it. Such a person wants to see Rust's design-decision guts spilled out on the floor before them, so they can move on if those decisions are not to their liking.
This doesn't mean the documentation isn't approachable to people who don't have any such points of comparison! But just like a movie can be enjoyable for both kids and adults on different levels, Rust's documentation has both a "teaching you Rust" level and another "justifying Rust's departures from the Average Low-Level Language" level.
Rust seems to have a very ruby-like approach in trading ceremony for conciseness, and while I feel I will eventually learn to appreciate that more, it does present a challenge while learning. It seems like there are a lot more symbols that have significant meaning in rust - where an average language might only have a few dozen or so "core" symbols that I must learn to use the language, rust has quite a lot more, and many of them follow idioms that I don't recognize from any other language (most of my experience is a pretty typical web dev background). It definitely makes learning by example hard (from actual userland code, not the excellent beginner focused hosted tutorial).
Although maybe it just feels this way because I'm on the start of a long journey of understanding and perhaps things look a little bigger than they are.
I don't know how the 2nd edition of the book deviates from the first one, but as you mention the strings section: I had installed rust with rustup, configured my editor environment, bookmarked and read tutorials and was serious about learning rust, dabbling with code snippets.
It was the exact chapter you mentioned, strings, which drew me away from rust in disgust. Four string types ... for the beginner? Seriously?
Do you mean "learning a system's programming language is not helped and may even be hindered by experience with general purpose languages"? Are there any good beginners' systems languages, which help Rust?
I agree, but I don't think it's inherent; I think it's a lack of resources targeted at this demographic.
While this is true, those languages have stuff Rust doesn't as well. Take Ruby, for example: Rust doesn't have method_missing, or eigenclasses, or inheritance. "How do I know which method gets invoked" is much more complex.
Compare that to, say, Lisp. My first programming class at Berkeley was taught in Scheme, which is basically Lisp. Within the first 20 minutes of the first day, the professor had not only gone over a bunch of introductory announcements, but he'd also introduced us to every bit of syntax we would learn in the course. The second lecture covered car/cdr and recursion. Beyond those two lessons, everything we learned was about the practice of programming, not the language we were programming in.
Rust occupies a fairly unique position of being high level, with lots of abstractions to make experienced programmers more productive, and low level with fairly precise control over lower-level hardware concerns. That's really impressive, but I do believe that impressive power comes at the cost of an unavoidable learning curve. You'll never get a backhoe to be as simple to operate as a shovel. Which isn't to say that there isn't ample opportunity for the work that you and Carol are doing to make learning Rust easier--beginners now have it much easier than people like me that jumped in around 0.8. But it is to say that I don't think it's possible to ever make Rust as easy to learn as conceptually simpler languages.
If I were designing a CS curriculum, I'd start new students with a Lisp dialect, then move on to Assembly (which, while hard, provides what is, to me, an essential view of the actual hardware that you're using) and then move on to the more practical languages. I'd still like to teach Rust, but it would come later to help introduce advanced concepts like compilers and operating systems.
This makes me think of the module reform RFC:
> In other words, while there are simple and consistent rules defining the module system, their consequences can feel inconsistent, counterintuitive and mysterious.
See also Rich Hickey's "simple made easy."
I do think Rust has some inherent learning curve, but I also don't necessarily agree with this framing. Simpler languages aren't always simpler to actually write software in.
I completely agree with this. But when you're learning to program, the goal isn't to write software. It's to learn the lessons and concepts that will one day allow you to write software.
Most people want to start "top-down", that is, write something useful. After they've got things working, then they want to "dive deeper" and go back down and learn the fundamentals. It's an spiral, not a direct path.
Another way to think about this is that bottom-up learning is the same spiral, but at a different level of abstraction. You're teaching software, but not hardware. You don't force people to learn about OSes before they learn about software. You don't force people to learn about hardware before they learn about software. You don't force people to learn about physics before they learn about hardware.
At least in my case I remember when I had C as a first language in school about 14 years ago, after an initial rush of joy, I got a bit frustrated because I couldn't build anything "cool" and practical quickly. I was relieved when we went on to the much more practical seeming Java. But these days I have all the tools I need to get stuff done quickly and prefer learning new things more bottom-up. In fact top-down often annoys me, because it presumes what I might find useful.
I think a mixed approach as often used when teaching musical instruments could work well. For example justin guitar online course splits up the practice sessions in parts that are tedious muscle-memory/rhythm training you have to push trough and parts to teach you songs from various genres as quickly as possible. So you can actually apply and most importantly enjoy what you are learning from week one. Additionally he strongly encourages doing explicit ear training and some music theory from the get go for the long term benefit.
Translated to Rust I think something like rustbyexample.com but instead of splitting it up by language features, have it categorized by domain and experience level would be pretty cool. If I'm interesting in game development I can start right away with simple cli games and later add graphics and if I'm into networked services I choose that path. Both point to the same deeper concepts but like when learning an instrument it is much more encouraging when at any level you can play the types of music you like instead of having to play what the teacher likes.
This doesn't seem like a very meaningful distinction to me. Lisp may not have a lot of syntax, but it still has a comparable number of features to other languages used in intro classes. You have to learn those features as you encounter them regardless of whether they come with new syntax or not, and the new syntax frankly isn't the hard part of that.
Rust certainly has more features than Scheme, of course. :)
The operative word here is actually 'taught' - I think it's just too much stuff to internalize to be able to read a few tutorials and get it. It's a topic for multiple semesters of structured teaching going together with lots (lots!) of practical excercises. In that sense, it's easier to be taught if you don't know anything about programming than if you have a wrong mental model of how programming languages work, because you have to unlearn it first or open your mind in some other way for a different way of doing things. (The post that I was replying to asked why would you want four different string types - and you actually need at least six to interact with C libs and the OS...)
I'm still relatively new to Rust but this is my understanding. Please correct me if I'm wrong.
`str` is very rarely stack allocated, and often points into a heap-allocated `String`. Further, there's an optimization on the table that will let `String`s point to static allocations.
The distinction is rather owned vs borrowed. A `String` is in charge of the memory it uses, so it can grow it by reallocation, free it when it goes out of scope, etc. A `str` is not in charge of the memory it uses- it's only a pointer into some memory that something else owns, whether that's a static allocation or a `String` or some other data structure.
> We’ll first define what we mean by the term string. Rust has only one string type in the core language, which is the string slice str that is usually seen in its borrowed form &str. In Chapter 4, we talked about string slices, which are references to some UTF-8 encoded string data stored elsewhere. String literals, for example, are stored in the binary output of the program and are therefore string slices.
> The String type is provided in Rust’s standard library rather than coded into the core language and is a growable, mutable, owned, UTF-8 encoded string type. When Rustaceans refer to “strings” in Rust, they usually mean the String and the string slice &str types, not just one of those types. Although this section is largely about String, both types are used heavily in Rust’s standard library and both String and string slices are UTF-8 encoded.
Higher level languages can get away with simplifying this stuff; systems languages can't.
At the basic level, there are two types, same as let's say C++ offers std::string, and char*.
Strings are really a hard thing to begin with, many languages simply obscure their complexity.
Also, somehow people seem to object less to Vec<T> vs. &[T] than String vs. &str.
It's great that owned string with contents on the heap is clearly distinguished from a borrowed view into a string. In C, when you see a char*, do you own the string or are you borrowing a string? The type doesn't say. In C++17, however, the distinction is present: std::string vs. std::string_view.
It's also great that Rust, unlike C or C++, distinguishes between strings that are valid UTF-8 and strings that came from a kernel that takes a GIGO position on encodings. Got a std::string in C++? Is it UTF-8 or garbage you got from the kernel? You don't know.
"An instance of this type is a static guarantee that the underlying bytes contain no interior 0 bytes ("nul characters") and that the final byte is 0 ("nul terminator")."
The functions that create CStrings also return a Result where you'll get an Err if your string contains any nul bytes. There's are unsafe variants that don't do these checks, but it's hard to predict what might break if this invariant isn't upheld. There's not much reason to use CString rather than just Vec<u8>.
This is where you've lost me, because Vec<u8> is precisely what one should use in Rust for passing around opaque buffers as you describe. I can't even imagine what sort of convenience methods one could implement on strings that deliberately have no specified representation!
A lot. Go's `string` type, for example, is conventionally UTF-8 encoded, but it is not enforced. Consider, for example, how much simpler this code[1] could be if `Vec<u8>` was more convenient to use as a string type.
There's a reason why the regex crate provides an API for both `&str` and `&[u8]`, because being able to deal with `&[u8]` as if it were a string is occasionally convenient. Importantly, without this API, ripgrep couldn't feasibly exist!
Other examples include file path handling. I need to be able to run globs (via regexes) on them, and the only way I can do that in a way that is zero cost on Unix is to get the bytes from the underlying `&OsStr` (on Windows, I do a lossy decode, which avoids the extra allocation in most places, but still requires the UTF-8 check). In particular, on Unix, this isn't even a matter of performance but rather of correctness, since file paths can contain arbitrary bytes and indeed have no specified representation! (Other than some rules like "no NULs and no /.")
It is often very convenient, in practice, to simply assume UTF-8 or at least an ASCII compatible encoding, rather than enforcing it as an invariant. For example, the link above to the ripgrep config parsing assumes the file contains ASCII-compatible text on Unix, and that's it. The file could contain latin-1 or UTF-8, it doesn't matter, and that is required for correctness. (Because file itself could contain file paths which may be arbitrary bytes.)
(To be clear, I think the UTF-8 invariant for String/&str was the best choice, certainly. What I'm saying here isn't that one should use Vec<u8> for strings in lieu of String, but rather, that using Vec<u8> as a string can be extremely useful in certain circumstances.)
[1] - https://github.com/BurntSushi/ripgrep/blob/7120f3225862f6c71...
1. As dbaupp said, there are some convenience methods which don't actually depend on a buffer being any kind of string, and would be just as useful for an &[u8] containing true binary data, or even arbitrary &[T] - yet currently are only implemented for &str. For example, find(), for finding the index of a substring/subsequence.
If that was all I wanted, though, I wouldn't be talking about wanting a separate type. So:
2. As burntsushi said, sometimes you have strings that ought to be UTF-8, but might be invalid. If they're invalid, I don't care about displaying the string correctly, but I still want to be able to round-trip between the source (perhaps an on-disk binary format, perhaps FFI with C/C++) and the native Rust representation, without crashing or doing a lossy conversion.
3. A similar use case is strings that could have any representation… that is a superset of ASCII. For example, this is typically a guarantee for locale character encodings on Unix systems - that is, if you want to properly handle legacy locales rather than just assuming UTF-8 (which reduces to the previous case). It also applies to legacy "text-based" formats and network protocols such as IRC. For some use cases, you'd need to actually know what character encoding is in use and convert between it and Unicode. But for many uses, you only need to locate, replace, split, or join based on known ASCII delimiters. And since the environment locale setting is not necessarily reliable (or in the case of network protocols, isn't meaningful at all), if you can do what you need with that alone, you should. For example, in the case of IRC, a low-level protocol implementation would ideally use byte strings for everything, and outsource the decision of how to decode messages to the client. It's not good enough to make the encoding a configurable setting: even if, say, the client wants to interpret all strings as UTF-8, if someone creates a chat channel whose name is invalid UTF-8, the client should still be able to ask the low-level implementation to join the channel, send messages to it, etc., which requires echoing its name back to the server.
This isn't actually all that different from the pure binary use case, since locating, replacing, splitting, and joining is also useful for arbitrary binary data. Unlike the "possibly invalid UTF-8" use case, you don't want any normal code paths to try to interpret the data as UTF-8. However, it would still be useful if the Debug impl did so.
What I like about the page is not that it makes strings easy to understand for a beginner, but that it doesn't hide difficulties about strings, that there are different ways to look at them and Rust doesn't presume a certain one. They say it isn't easy, then go on explaining why.
To me this makes the steep learning curve feel justified. Where as some other language tutorials brush away any difficulty by saying "you don't need to understand that, just trust us."
This is also a matter of perspective. I'm not looking at Rust for general web development (at least not as main language) but for more lower level stuff I want to get into. And least to me as a beginner in that domain it sounds very believable that those distinctions matter in that context
We had a discussion on the strings thing the other day: https://news.ycombinator.com/threads?id=steveklabnik&next=16...
TL;DR, the language only has one string type. The stdlib has more. They’re all needed. Strings are hard.
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
This is such a beautiful sight for anyone who remembers the bad old days of Extended ASCII.This brings back PTSD for anyone who implemented ad-hoc RTL and UTF-16 surrogate pairs support in an environment that lacked them. Thank g-d I insisted that we at least omit diacritics as nobody uses them and they're a huge pain to implement and QA properly.
Also, can we please, please, please keep source code files as pure ascii? Asking as a non-native english speaker. There're a lot of tools that we developers need to use on those, and their encoding issues tend to follow the Murphy's law and appear at the most inconvenient moment.
Edit regarding localization: all projects that I've worked on had localization in separate json or XML files, with automatic import/export from spreadsheets that would be filled by third party localization company.
This is the line of code I find my cursor on literally the moment I switch to HN:
questIcon.Text.text = ("Quest:" + World.current[index].template.trueId + "_title").T();
Where T() is the extension method from localization system. Why on earth would you include user-facing strings in the code itself?
How do you propose that we write programs that interact with non-ASCII compatible language users? All string literals are to be saved in external, somehow non-source code files?
How would you go about writing a Chinese "Hello World"?
In Python 3, I would write
print("你好,世界!")
I have no idea how you propose this should be done in pure ascii. print("\u4f60\u597d\uff0c\u4e16\u754c\uff01")
I'd guess? print("\u4f60\u597d\uff0c\u4e16\u754c\uff01") # "你好,世界!"You'd obfuscate the content, but to be practical you'd still have to know what it said.
Hence my comment.
So you'd probably write something like:
print(localisedStuff["zh"]["hello-world"])
This looks clunky in a little standalone example but if you can imagine having to build an app that supports many languages then this starts to make sense as you'd more likely have: print(localisedStuff[selectedLanguage]["hello-world"])
However I disagree with the original comment that non-ASCII chars shouldn't be supported in code - I think that Chinese, Arabic, Korean, Israeli (etc) speaking devs should be able to deal with string literals in whatever language they feel comfortable in.Your example would be:
print(_("Hello World!"))
And then a zh.po file with: #: file.py:1
msgid "Hello World!"
msgstr "你好,世界!"I guess that's one good thing about Java's resource bundles, though they don't help source readability.
I'm a non native English speaker. That's why I hate tools, and websites which don't understand that people have different letters in names. Just use good tools.
Even huge sites have problems when I want to use my real name, or the name of the city I live in. They are usually made by US companies, which is strange, as you have all kinds of people there, with different names. Unfortunately the reality is that those US companies don't care about supporting normal characters people use. Even websites like Upwork don't allow me to enter my real name. Isn't that strange? I have told them about that months ago, nobody cares.
So getting to the real problem: supporting unicode is not as hard as it seems, just use from the beginning.
Retrofitting unicode support onto existing programs however is a hard problem
for all unicode-lacking but otherwise well-developed tools, with no sufficiently useful and also unicode supporting alternive, that you'd like to use, whats your proposal?
Go back in time and tell them to use unicode from the start? Reimpliment them yourself? Give up and say no useful tool for me?
There are a hundred reasons certain tooling isn't viable for your usecase; unicode support is rarely even close to being an immediate dealbreaker. Particularly because it can be sufficiently worked around.
Exhibit A: latest GCC error message,
"le déréférencement d'un pointeur transtypé à la barbare va enfreindre la règle selon laquelle une zone mémoire ne peut être accédée que par un seul pointeur".
Nowadays I just set LC_ALL=C on my machines since translations are so bad.
But why? In scientific computing (e.g., in C and in Python), I like to write my formulas using variable names like α, β, γ. Having to write alpha, beta, gamma uglyfies the formulas unacceptably.
Support for non-ascii variable names is actually my main reason to use clang instead of gcc.
If there are tools that break when this happens, those tools need to be fixed. Bugs in those tools need to be found (through usage) and reported. If we avoid the problem by strictly sticking to ascii in source files, that amounts to sweeping those bugs under the carpet.
Furthermore, another commenter said "use good tools". The inverse of this—don't use bad tools—is important: developing tools that correctly handle encoding issues needs to be seen by developers as a priority. There needs to be a ramification (in the form of bug reports, complaints, etc.) if encoding issues are not considered by tool makers.
Technical debt, like any debt, shouldn't be blindly avoided, but instead managed in a smart way. But anyway - it is not my debt or problems to solve. Most of the tools I'm talking about are closed source, and I wouldn't be able to do anything with them except hacking around.
I'm genuinely curious (this is a straightforward question): have you personally gotten applications you've written to compile yet, or are you still learning theoretically?
(I haven't used rust, but have read a few comments about a high learning curve.)
Rust does have a high learning curve but to me it seems justified by its goals, the domains it targets and using some concepts that are unique or not the most common. Still I'm fairly confident I would get a simple CRUD web app out in a day if that's what I wanted. Learning curve is not crazy high, just higher than the usual high level languages.
My background is mostly in Python/js web development, but I'm currently learning dsp for audio processing and synthesis. And since language doesn't really matter at my current level I alternately implement things in Python, puredata, C++ and Rust. So I won't be implementing full fledged applications for quite a while :)
Starting at the end, a DSP is definitely what I mean by "application" (the reason I used the word 'application' is to exclude a straightforward 'hello world' copied from the exercise book.)
For example, have you gotten any audio processing application to work ta you've written, or anything like that?
I'm surprised you mention CRUD web apps, as I didn't think Rust was used in that domain much. (i.e. on the server side like php or even Go might be.)
In dsp on the other hand I'm starting completely afresh and more time is spent learning the fundamentals and brushing up on the math than coding. I know pretty well what kind of things I want to build and understand the higher level concepts, but don't know yet how to get there. Meaning at the moment Rusts' learning curve doesn't get in the way of reaching my goals.
What I have done so far in Rust is some simple wave form generation and modulation and writing it to wav files (via an encoder library). Which kinda is the audio equivalent of "hello world" so I wouldn't call it application.
My first actual application I'm aiming to build by the end of the book, is a basic midi playable polyphonic synthesizer with the usual features and some effects. If this is realistic or not, I don't know. But I reckon if Rust gets in the way it is not because of the language but lack of resources and available libraries for the domain.
I guess you'll get to play with the FFI capabilities by incorporating some C libs then. :)
Different people find different things hard.
It still isn't done. Like a good wine I guess.
> Asked how long the string is, you might say 12. However, Rust’s answer is 24:
I get that, `.len()` is tricky, but why not include an easy, common way to determine character length of a string in the documentation?
“Character length” is not a Unicode concept, for example.
Now reading the promise to get a stable async/await and also having Tokio 0.2 in the pipeline, I'd say async Rust might be ready for prime time.
Don't get me wrong here, I enjoy using Tokio, but I'd say if you're not very experienced yet with Rust, and you try to mix an asynchronous database query and a web request together in the same event loop, or you wanted to use a reference to `self` in an inner future (which might suddenly want a static lifetime), you quite easily end up into trouble... Trouble like a couple of pages of errors, that are just not readable at all. And you get out maybe by knowing to use a `map_err` in that one future call after the second `then`, or by not referencing `self` in your futures...
Now when reading the announcement, and I've read and understood the RFC's, the next generation of async Rust will help here tremendously. And I will definitely be amongst the first ones adding the changes to my code, to finally make it cleaner and nicer for the next developer to jump in.
What I hadn't considered: specialized syntax leads to better error messages for the most common cases. That's probably quite a good thing, especially since descriptive error messages make me much more productive. (Not to mention that specialized syntax also acts as a type annotation of sorts too; a common problem I have in Haskell is figuring out _which_ monad a certain `do` block is using.)
Does type signatures not help in that case?
(do x <- pure 1
y <- pure 5
pure (x + y)) :: Maybe Int
It isn't uncommon to have to scan the `do` block for some statement that constrains the monad somehow.This is a known and discussed problem, so I'll refer you to the article usually reference around this issue: https://wiki.haskell.org/Do_notation_considered_harmful
flip runStateT s $ do ...
To find the type of this do block I don't have to scan it I just have to look up the definition of runStateT.If I have to add an explicit annotation to a do block it's simple enough to separate it into another function.
justSix :: Maybe Int
justSix = do
x <- pure 1
y <- pure 5
pure (x + y)
I read the article and couldn't find the part where they said that type inference is a problem with do notation.I would claim that such an abstraction is fundamentally incompatible with Rust.
Not only does Rust lack the means to write a Monad trait on which to build do-notation, but even given HKT there's no single type signature that the various monad instances would fit. `Result` and `Option` are type constructors while `Iterator` and `Future` are generic traits; some instances require `Fn` or `FnMut` while others require `FnOnce`.
And even assuming those problems could be solved, the way do-notation interacts with control flow is not composable with idiomatic Rust. Nested closures prevent the use of return/break/continue and imperative loops; Haskell doesn't have those features so people just lift their functional counterparts into monads. Monad transformer stacks make the situation even worse- they are a pain to compose even with each other, let alone imperative code.
If you want a unifying mechanism for this stuff in Rust it's gonna need to be fundamentally more powerful than monads. Scoped continuations, maybe? Certainly nothing that looks like >>=.
Java and Javascript, for example, have been gradually introducing monadic concepts into their ecosystems. Java collections and Futures grow 'of' and 'flatMap', JS Promises aren't quite purely monadic but have 'resolve' and 'then'. So it's perfectly possible for an imperative language to use monadic features to implement async operations.
But much as I enjoy using JS Promises (really!) I prefer ES6 async/await, even though it's just syntactic sugar. Not least because even fairly straightforward-looking async/await code can desugar to something you wouldn't want to try to read by hand.
What I'm talking about is a single Monad abstraction to tie these all together. I mention do-notation because it's one example of something you would built on top of such an abstraction, and one way to bring together the seemingly-disparate collection of `?`/`yield`/`await!`. In Haskell, for example, you can use do-notation not only for IO, but also for async code, and list comprehensions, and early returns, and a host of other things.
There are more convenient ways of composing monads than monad transformers; extensible effects are a lot easier to work with.
Ulttimately I agree though, monads are not the right abstraction for a systems language. I'm quite happy using them in languages like Haskell and OCaml, where you've not only got a garbage collector solving the tricky ownership qustions for you, it's also faster than it has any right to be, so you can basically ignore the overhead of heap allocating a closure.
They become much less attractive when you're in a problem domain where you actually want to worry about fiddly details around memory allocation. This is much smaller space than people think it is, but it's what Rust is for.
I'm not sure how they'd give you a unified mechanism to implement these kinds of things, though. The Monad typeclass lets you implement new instances in libraries. Is there any work on defining effects like async/await in libraries? If so I imagine they'd still have to use something like continuations.
This is indeed very frustrating. I've had to learn to translate between "what the compiler is telling me" and "what the issue actually is".
A lot of companies have tried to make products that are both infinitely backwards compatible and always supported while still introducing breaking changes like this. It quickly becomes a giant internal mess of trying to figure out what the most common denominator of feature requirements to implement new things is. Developers will start making ergonomic decisions in their changes (well, it could support 2021, but I want a 2024 feature so I'm going to use it and peg this feature to 2024 minimum) and new changes need to be tested against every different language iteration.
This might seem like the best answer now, I just hope its not biting off more than anyone can chew in 2030. C++ had an era of malaise where conflicting implementations of the standard library across a dozen+ platforms led to the complete abandonment of it by the mid 2000s and it took Herb Sutter and friends to bring C++ back from the dead with one unified single truth of C++ with concrete compiler flags and tons of caution before adopting anything new to make sure all participants were capable of fielding it quickly enough to avoid the ecosystem fragmentation of the past.
Rust is advantaged today with only one major implementation, but if mrustc or other alternative Rust compilers become popular mixing those with epochs / eras / editions can spell disaster for language portability.
Its not an impossible task, but it is definitely a huge one. And its not the kind of work hobbyists will ever be willing to put up with in the long term, and its the kind of work that requires extraordinary familiarity with the whole of rustc, its design process, the RFCs, LLVM, etc. Its its opposite kind of work to what you put on the easy issues list for new contributors.
It is the primary reason why most software staggers its releases and depreciated support. Why Microsoft dropped Windows XP at all despite its wide adoption. Because even with billions of dollars of cash on hand trying to keep multiple parallel interconnected but distinct code paths functioning while both securing and improving them in isolation or together is in the highest echelons of software complexity and difficulty.
> Heres just hoping that Mozilla is prepared
Rust is an open source project Mozilla contributes to heavily, it's not a Mozilla project. This is the Rust project's problem, not Mozilla's problem.
(For example, Mozilla employees, of which I am one, are a minority in governance these days. We're the largest single group of people by employer, but are at 50% representation on the core team and are something like between 10%-20% of the rest of the teams, which have just as much jurisdiction over Rust as the core team does.)
> It quickly becomes a giant internal mess
This is why, as noted elsewhere in the thread, editions can only tweak the frontend of the compiler. By the time you hit MIR, the main intermediate representation, all edition differences are gone. This is for both compilers and humans. As you rightfully say, if we allowed arbitrary changes, it could significantly increase technical debt. But it'd also be very hard for humans to understand as well. We won't be fundamentally changing what Rust is for these reasons.
> Rust is an open source project
So is Perl6
What are you talking about? C++ has continually been in the top 5 on the TIOBE list.
https://www.tiobe.com/tiobe-index/ https://www.tiobe.com/tiobe-index/cplusplus/
I haven't played with Rust but I like the ecosystem and transparency.
Something that stands out is the Compatibility across editions section. Seems like they really thought this through:
> you can be on a different edition from your dependencies.
> Edition-related warnings...must be easily fixable via an automated migration tool (rustfix). Only a small minority of crates should require any manual work to opt in to a new edition
> the progression of new compiler versions is independent from editions; you can migrate at your leisure, and don’t have to worry about ecosystem compatibility; These are huge!
This really helps with ecosystem fragmentation. Other languages take note (please!)
Another thing that stood out was the embedded as first class citizen:
> Embedded devices. Rust has the potential to make programming resource-constrained devices much more productive—and fun! We want embedded programming to reach first-class status this year.
This seems like the only thing on the list that might not finish in time for the year or will be sub-par. I don't know how much more work they need to do but embedded programming requires fine-tune memory management and access to special registers on a per-hardware basis. A lot of times these things fly in the face of assumptions many programming languages (and compilers) make
> might not finish in time for the year or will be sub-par.
Yeah, these areas of focus are where we think we can make substantial improvements, but it may be something like how the webs services goal: last year we shipped fundamentals, this year we're shipping end-to-end awesomeness. So we may only get the fundamentals in place for this year, and maybe we'll need to focus on it next year too. We'll see!
The post isn't really clear what will be gated behind editions though, only non-BC syntactic changes (e.g. literally just new keywords)?
Still, I think it's short and simple enough that it could have been noted in the section, which I feel currently talks about editions a lot but doesn't say much about them.
- new keywords https://internals.rust-lang.org/t/keywords-to-reserve-for-ru... . There are a lot, but not all of them may end up being used, this is conservatively reserving them. In case a crate on a previous edition has e.g. a method called `catch`, you can still call it via the "raw identifier syntax", `foo.r#catch()`
- you can no longer call methods on raw pointers that do not have a fully concrete type in the new epoch. E.g. `foo.is_null()` won't work if `foo` is `const T` and `T` isn't fully known. (This paves the path for `self: const Self`, which may be introduced later).
LTS is an entirely different axis. LTS is about artifacts, editions are about the language.
I'd really like to see us start an LTS policy this year, but we'll see.
Is this going to be tracked through the system that catches compiler regressions against the entire crates.io ecosystem?
> Diversity and inclusiveness continue to be vital goals for the Rust project at all levels.
I mean.. lifetime checks won't stop working if you don't consider yourself heterosexual or anything o.O The two matters simply don't mix in my head
It is important for projects, now, to state their goals upfront and be clear, especially when you could have 1000s of contributors from all over the world. This is something that's new for software projects, but as open source becomes larger and more diverse its becoming more necessary. The alternative has been a community based loosely around some invisible set of rules that alienates or drives away potential contributors or sparks some drama that ends up on the top of Reddit/HN/etc.
Lifetime checks will stop working if the project gets abandoned over political issues.
Because.. why excluding people based on their skin color is discriminatory, but including people based on their skin color is not discriminatory?
> the unjust or prejudicial treatment of different categories of people, especially on the grounds of race, age, or sex.
Is it unjust to make sure people from a minority do not feel alienated?
I do, really do, belive this should just boil down to: - don't be a jerk - don't discriminate
That's what we thought before Rust came, that you could only have safety if you sacrificied speed. But now we know it was wrong ;)
Secondly, one can't be sure that others won't feel alienated. You just put them in a different classification. I would definitely tend to feel alienated..
So, yeah, it's a political issue, but an open source project is necessarily a political entity by virtue of being made of people. You can't escape making political decisions, and since "inclusivity" mostly just means being nice to people, I can't complain much.
In other words, "not excluding" implies not asking for color skins.
They have talks, documentation is awesome, examples may even run online, ide stuff is cool, In my opinion, it hard to be more welcoming than that, and special treatment based on any individual traits (except for "what OS you're using" and stuff) would not only be unneeded, but also should be repelled ([subjective] personal value).
They will say that they value diversity of thoughts that may come when you have people with various backgrounds, but it is much more primitive in practice, always boiling down to companies acquiring people with certain skin color or gender just for the sake of it.
There are well-known but taboo reasons behind the lack of so-called diversity in tech and other highly intellectually demanding areas. And as long as uncomfortable truth cannot come out in the overly sensitive cultural climate of today, initiatives and policies in this direction will remain misguided.
Let’s make it simple - having a diverse and welcoming community makes a project more attractive to contributors, and means that it’s more likely to succeed in the long term.
Sure, I never argued against fostering a welcome environment for contributors. We need more of this especially in open source, where contributions can be sorely lacking.
Instead I question the notion that Rust needs to have some sort or diversity to succeed. The assumption that it does implies that without diversity Rust will fail. However, Rust is not particularly diverse at the moment and it is certainly successful.
Are contributions currently being rejected based on personal traits?
I think the answer to this question would help me a lot in understanding any of this.
You could read this: http://www.latimes.com/business/technology/la-diversity-righ...
Or maybe if you like listening instead: https://www.gimletmedia.com/reply-all/52-raising-the-bar
So, while it's a nuanced and complicated topic, there are actual reasons why this benefits a project like Rust. Diversity amongst any organization is not only good for society, but objectively the output of the org itself.
but you think that the first one, based on your own judgment, will be more "out of the box" when having solutions for the company.
So no matter what you think, the one from a minority is certainly preferable for hiring?
I think the idea is to include as best as possible what may be hard to assess factors such as difference of experience, culture and upbringing and using that, make a decision as the hirer who you think will benefit the company best, not to choose a person and then override who you think is best just because of one factor.
All this really takes is valuing an outside perspective. An yes, sometimes just the skin color can be enough, if that skin color has affected how people see them or any unique problems it has come with. That does mean it is or must be enough though, just that it may be something worth including.
As a simple example, I'll leave you this: How We Accidentally Made a Racist Videogame.[1] It should be easy to see how a slightly more diverse group of developers and/or QA testers could have avoided that problem. Similar things exist with dealing with other cultures or countries, or people with disabilities, or if you're really unlucky, just people of the other sex if you're unlucky enough to have that much of a homogeneous set of coworkers.
1: http://www.kotaku.co.uk/2017/01/12/how-we-accidentally-made-...
Once you see that worthy men can be made to feel unwelcome, merely due to their choice of programming language, it makes one wonder if other worthy people do not feel unwelcome when they are treated dismissively in some other way, as by by referring to every one as “men” indifferently.
For me that's the biggest issue currently with Rust. Type inference via "let" only makes it harder as I can't really know what type a certain variable or expression is without carefully tracking docs. I've tried two Rust plug-ins for VS Code and even though they installed required dependencies I still couldn't have proper list of members when pressing "dot" and without that I feel like programming in the 90's.
Also sometimes I just let the compiler tell me by forcing the let variable to something that it is obviously not:
`let not_a_string: String = get_something_that_I_have_no_idea();`
error [4242]: expected type String, found type &*YouWouldNeverHaveGuessed
Ha! Very clever! Thanks.
It’s a trick I used quite a bit when playing about with rust, and Kotlin actually.
Alt + Enter should invoke intentions pop up, you can do it from there. (I think)
VS Code's handling of TypeScript really spoiled me but on the other hand it seems like tooling becomes more and more important in programming languages (e.g. Roslyn).
I'm excited about this. In my experience, embedded (and by extension, a large part of kernel/driver programming) is the last stronghold of C, because it's a place where you really need fine-grained control of your environment and C gives you that by default.
It's my day-to-day workhorse, and I often feel like I'm stuck in the dark ages still dealing with issues of tooling and expressiveness that CS has solved long ago. Free me!
[1] https://internals.rust-lang.org/t/announcing-the-cli-working...
I spent a long time working mostly with dynamically typed languages, but over the past few years, I have slowly drifted back into statically typed territories. At home, I have used Go a lot, at work, I have grown to like C#. Rust kind of looks like the next logical step along that trajectory, doesn't it?
And I get the impression that people who took the time to learn Rust enjoyed it very much. So here's my delayed new-year's resolution (I am the ____ing king of procrastination!): I am going to learn Rust and I am going to like it (I hope)!
Lots of great stuff coming this year! As always, happy to answer any questions.
We expect most people to say "Rust 2018" and not use any of these terms; we still need one to be the key in Cargo.toml and the flag to the compiler, so we couldn't just use nothing.
But yes, it could.
Generally the trend seems to be to still try to avoid having actual breakages in the editions unless we _have_ to, and this seems like one of those cases where we don't.
That said, adding Ord::clamp doesn't technically count as a breakage for Rust, Rust's stability guarantee does not include "we added a method to this trait/type and now you have to disambiguate via UFCS" or "we added a thing to this module and now you have to alias your import" because it's not really possible to have that stability guarantee and continue to evolve the stdlib.
That said, given that Ord is in the prelude, the bar for adding stuff to that is much higher. And Rust also cares about the impact of not-technically-breaking changes like these, so we definitely wouldn't just add it as a method.
This is great to hear! A best-effort backwards compatibility guarantee is important to have for a language that has a lot of development.
Editions can do two things:
* add new keywords
* make lints go from warn to deny
Both of these things are "breaking." An example of the former is the "catch" keyword, allowing you to use ? inside a block rather than for the whole function body. An example of the latter is the module changes; the older style will be a warning in 2015 and an error in 2018.
Though to re-emphasize the OP, all such breakage is opt-in (via compiler flags and/or Cargo.toml), and crates on different editions can be mixed freely. There's no enforced migration or involuntary backwards-incompatibility.
However, to be clear, editions are primarily a marketing/communication/project management tool. They give us a way to try to bring together a number of threads of work into a coherent whole, with a high level of polish across the board (including docs and tooling), and then to present that work to the world with a clear story.
It's worth comparing this to the recent Firefox Quantum release, which was likewise a singled out release on the normal train process that brought together a number of important changes, marketing/communication, and a high level of polish.
> It's worth comparing this to the recent Firefox Quantum release
I don't think it's a fare comparison. Firefox Quantum doesn't involve much of breaking changes (if you don't consider deprecating the old plugins one, that is). And the changes are most drastic since early versions of FF. With Rust though, it's clearly improving every day, I don't see Q3 2018 as any sort of "quantum leap". At least for as long as it doesn't include the const generics :)
That is what editions are, though (though as I keep insufferably emphasizing, all breakage is opt-in). Can you be more specific about what you're afraid of?
> Firefox Quantum doesn't involve much of breaking changes (if you don't consider deprecating the old plugins one, that is).
You must not have been on any web forum discussing Firefox 57 if you don't remember the cacophonous, world-ending drama that deprecating legacy add-ons created. :P
FWIW, this was part of the story from the beginning (https://github.com/rust-lang/rfcs/pull/2052)
> Firefox Quantum doesn't involve much of breaking changes (if you don't consider deprecating the old plugins one, that is).
They weren't just deprecated; they stopped working.
> I don't see Q3 2018 as any sort of "quantum leap"
For people following Rust closely, the Rust 2018 release won't be so special. But most people aren't keeping up with the details of new releases every six weeks; for them, we have an opportunity to take stock of what's changed over the last couple of years, explain the impact on idioms and how to transition code.
Remember that Rust is fairly unique in having a six week release cycle. Most other languages have a much slower release cycle, where every release is "major". Editions give us a way to pull together the rapid work we're doing into a coherent story.
From a management perspective, it's also a very useful way of focusing our effort as a community, to make sure all the pieces we've been working on are coming together into a polished whole on a clear timeline.
I honestly can't think of a bigger breaking change that FF could have made.
I'm interested in seeing how Rust and Go will compare in the future of the web.
Who will win? ;)
In my experience, it's been easier to integrate two languages that have GC (JavaScript and Go) than two languages where only one is GC'd (JavaScript and Rust).
What will the interop with Rust and JavaScript look like? How will pointers be managed?
I played around with both Go and Rust from Python and the garbage collection made things harder not easier. Now we write lots of Rust modules for Python and managing memory is pretty easy. We have Python wrapper objects that keep one pointer alive.
It sounds like you're mostly worried about memory leaks, but the primary concern with trying to get two separate GCs to play nicely isn't leaking memory, it's double-frees (both GCs trying to collect the same resource) and use-after-free (code in one language trying to use a resource that's been accidentally collected by the GC of the other language).
IE6 had exactly that issue, the DOM was implemented using COM which has its own GC (refcounting) separate from the regular JS runtime.
If you created a cycle between DOM and JS by, let's say, adding an event handler to a DOM node — thankfully that's something you'd never do when developing web pages & applications — you'd start leaking memory which would survive across page reloads.
There was actually a bit of software called Drip, it was just a webview wrapper (technically an MFC app with a browser COM component), you'd launch it, open your page, click the "blow memory" button and it'd reload the page over and over again listing elements which had survived between reloads.
First, a disclaimer: all of this is extremely early days, and subject to change.
There's two questions hidden here: "what does the workflow look like?" and "how does this work technically?"
Currently, "what does the workflow look like" is this: https://github.com/ashleygwilliams/wasm-pack#%EF%B8%8F--how-...
"how does this work technically" is this: https://github.com/alexcrichton/wasm-bindgen
The former provides a nice workflow that's fundamentally built on the latter.
By contrast, for a language like Go, it's probably more practical to ship entire apps, rather than embedding in libraries, because there's significantly more overhead in terms of runtime system support.
Developers. :)
Native WASM support isn't there yet, but if you want extra speed you could combine Nim's JS backend and Nim's C backend + emscripten to compile to WASM. You can see this approach in action in a game called Reel Valley [1][2]. The advantage is that you can access the DOM from Nim using the JS backend (you cannot do that in WASM yet AFAIK).
> (you cannot do that in WASM yet AFAIK)
wasm can do anything JS can by calling out to JS. What people mean when they ask for "DOM support in WASM" is to have it be direct, skipping the need for the JS call.
https://crates.io/crates/stdweb is an example of using WASM to manipulate the DOM through JS in Rust today.
So all languages will win, with the exception of JavaScript.
If browser vendors keep moving the browser into a general purpose VM, lets see how long JavaScript keeps being the "browser systems language".
I'm not a guru, but I've used a fair bit of F# and modern C++ in a commercial setting, so a lot of things in rust weren't too surprising. I learned about the different kinds of strings (they make sense, even if they're poorly named). I learned the strict borrowing rules. I learned the strict mutability rules.
But try as I might, I can't bring myself to care about the difference between the ~190 different types of iterator[0].
I think I should just be able to use a Iterator trait, but I can't get any of those concrete instances to match up. It very much triggers my "I don't care reflex" in a way the other stumbling blocks really didn't.
Using concrete types for every adaptor means all the steps in a iterator transformation pipeline are statically known and (importantly) obvious to the compiler, and thus is highly amenable to inlining/optimisation to get it down to something close to a plain 'for' loop. If you're willing to forgo that performance, it's possible to just put everything behind a pointer/virtual call with a Box<Iterator<Item = ...>>, which ends up more similar to sequences in F# etc.
It does seem as if the motivation for this design isn't discussed in the documentation.
Any notes on const generics e.g.`Array[T, N]`?
Note that all of this should allow impl<T, const N: usize> Trait for [T; N] {...},
but not actually passing a constant expression to a type/function, e.g. ArrayVec<T, 3>.
What I'm actually looking is `Array<Y, size_of::<T>>` for support for data structures that vary depending on size of `T`.There do seem to be some proposals to add threads, though. Also, it's possible (but hard) to build cooperative multithreading on top of it; apparently Go is going to do that.