I started learning Rust over the weekends and I think the second edition "The Rust Programming Language" is among the best introductory books on a programming language I have read (well half way so far).
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.
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.
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:
> 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
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.
This is all good and well, but there are also projects that need to be delivered in this imperfect world and imperfect tools that we have. And I'd rather assume the worst of the world in the beginning and take appropriate precautions than debug some tooling problem right on the day of the release.
You're right, economic incentives do usually favour ignoring/avoiding difficult problems and increasing technical debt. but I'd generally like to approach that as a pragmatic balance between what should be done and what needs to be in the short-term.
> ignoring/avoiding difficult problems and increasing technical debt
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.
> Also, can we please, please, please keep source code files as pure ascii? Asking as a non-native english speaker.
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.
I'm also non native english and I would much rather have people focus on fixing bugs rather than submitting idiotic translations.
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.
Just today I was trying to install a new version of Postgres on Windows. For some reason they decided to detect system language and print all error messages in Russian... in the wrong encoding, so it looked like lorem ipsum written in Wingdings font. Took me ages to understand what the problem is. Just use English for god's sake.
No, there is no justification for bad tools. If someone makes a tool only for ASCII, fine, but don't expect that people will say it's a good tool.
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.
>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.
> Also, can we please, please, please keep source code files as pure ascii
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.
I think the main point is to remove the Chinese characters in the string literal and refer to some object which contains the contents of some already-parsed localisation file - and that's the file where the Chinese chars characters would live.
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:
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.
> Also, can we please, please, please keep source code files as pure ascii?
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.
I'm not sure—I think it's more, in this case, that Rust assumes the developers learning about it have specific goals: namely, to evaluate Rust vs. C/C++. So every resource that teaches Rust couches the discussion in terms of justifying why the particular design being presented is better (from the Rust maintainers' perspective) when compared to designs in existing languages, even if it makes other sacrifices to be such.
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.
> 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++.
Sorry if I wasn't clear; I didn't mean to imply that the Rust maintainers assume any prerequisite knowledge of C/C++.
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.
I also just started learning rust over the past few weeks. While I'm still grasping with some of the core concepts of the language (like ownership), I do feel like there's adequate documentation and resources out there that I'll get there. What I really like is how helpful the compiler is. It's fast and the errors feel well explained and relevant to the problem in my code. It almost feels like I'm developing on a REPL.
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.
There's definitely a lot of syntax that makes the language look bigger than it is. When I realized Rust's oo-style dot notation/self/Self was just sugar for plain old function calls, suddenly a big chunk of the language I thought I hadn't learned yet turned out not to just not exist.
I haven't looked at the first edition, just wanted to be clear which version I'm referring to. Kudos to everyone involved and Carol for the improvements to the second edition:)
That book indeed is really really good. Though i have one feedback, they should have solutions to "Extra problems" they have at the end of chapter (maybe separately). Have been stuck at closures for couple of days now, where you are supposed to implement your own cacher with hashmap.
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?
There are way more than 4, but each one makes sense and has a purpose. Trying to conflate them would make things conceptually less clear.
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.
The only thing that bothers me is that Rust’s standard library has no good solution for when you do want to take a GIGO position, such as if you need to interoperate over FFI with C/C++ code that does so. There’s Vec<u8>, but it’s missing a lot of the convenience methods and other functionality available for strings, and its Debug impl (i.e. string representation for debugging) gives you a list of numbers rather than, say, a best-effort UTF-8 interpretation. There’s also OsString on Unix, but that’s OS-specific and also is missing functionality. Unsurprisingly, there’s a third-party crate that has the functionality what I want, and thanks to Cargo that’s almost as good as it being in the standard library… but I still want there to be something in the standard library. :)
> There’s Vec<u8>, but it’s missing a lot of the convenience methods and other functionality available for strings
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!
> 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.)
Yeah, my use cases are a combination of burntsushi's and dbaupp's answers.
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.
One might have strings that have unspecified, but identical, representations, meaning one can do things like 'split' and 'replace' (in general, Vec/&[] doesn't have great tools for operating on subslices, like those two string functions: essentially just 'chunks' and 'windows').
Yes, that's what I meant. Actually, both the Unix variant, "GIGO byte sequence probably UTF-8", and the Windows variant, "GIGO u16 sequence probably UTF-16", have cross-platform use cases. For example, the UTF-16 variant could be used when doing FFI with Java, or when reading Windows filesystems. Therefore, I think ideally Rust would provide both variants as cross-platform types, and OsString/OsStr would just be aliases for one or the other depending on the current platform.
Naive question from someone who's never written FFI code: does std::ffi::CString work here, or is the issue that you need to be able to have null bytes within the string?
"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>.
I'm coming mostly from Python for web development, so I haven't spent much time of my life thinking about strings. But I also usually have not really any clue what is going on under the hood when working with them. Which is fine, because most of the time it doesn't matter (as long as you don't do a+b+c... too much) in my work.
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
Your comment is confusing because they only mention two common string types, and then clearly explain the difference between the two (coming from someone who doesn't write any rust):
> 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.
Rust in general isn't really a beginner's language. You have to understand computers at a very low level to appreciate the design choices. Given some dedication, it'll help you at that but it's not easy, especially if you already know some higher level languages. It's probably easier to teach it as a first language than a second after Python or Java.
Can you clarify your meaning? I'm a little confused. It sounds like you say "Rust isn't really a beginner's language... it's probably easier to teach [Rust] as a first language than a second after Python or Java". At first it sounds like a contradiction but when I inspect closer I realise there's alternative interpretations.
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 think your parent is saying "Rust isn't a great language to learn programming with", that is, if you've never programmed, Rust is not a great choice.
I agree, but I don't think it's inherent; I think it's a lack of resources targeted at this demographic.
I do think a lot of it is inherent in the design choices that Rust has made. Rust, as a language, has a fairly large surface area. There's a lot of different syntactical and conceptual lessons that need to be learned by new Rust programmers.
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.
> 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.
> 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.
This is probably where we disagree. The style you're advocating is "bottom-up" learning, that is, you learn the fundamentals, and then build from there. This is totally valid, but in my general experience of years of teaching, both professionally, and as a hobby, most people don't learn like that. I personally learn like that, so it's a bit frustrating to me that this is true!
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.
Do you think this depends on the peoples prior experience level whether they prefer either way? Or is mostly personal?
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.
> he'd also introduced us to every bit of syntax we would learn in the course.
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. :)
While the book is really top notch (I'm usually not a fan of programming language books), there's something to be said for not introducing all concepts at once. A scripting language has many of the same concepts, but not lifetimes, etc. Not to say objects are intuitive!
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.
Okay, I yield. I think you may be right. I've been thinking a lot about how I'll teach my children STEM topics and I've grappled with the idea of teaching Rust. One of the things I like about it is that you know the behavior at compile time, unlike Ruby or even C.
Steve got it right. I'll clarify nevertheless: Rust and computer architecture (caveats apply) are (IMHO) very good subjects to be taught in tandem, as Rust tries very hard to provide safety features and high level abstractions that are impossible to implement in general, but quite possible in special, if very common, cases. Seeing where it is possible and where it's not and why should be an eye-opening experience.
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...)
Where are you seeing four string types? As far as I know, there's only `String` and `str`. Heap allocated vs. stack/statically allocated. There's also things like `CString` and `OsString` but I don't think I've had to deal with those. If so, it was so similar to working with any other string that I didn't even notice.
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.
You're right; I kind of left that out since it's not (currently) possible to have a bare `str` anyway and there are more ways to work with one than just `&str` (`Cow<str>`, `Box<str>`, `Rc<str>`, etc).
There are two string types in Rust: String and &str. The CString and OsString types only exist for FFI and platform-dependent interop (because unlike Rust's string types, there's no guarantee that the rest of the world is UTF8-encoded). The only time you'd use CString and OsString (or see them get used) is at the very edges of your system, and you'd convert them into String or &str as soon as possible (or Vec<u8>, but that's hardly a "string type").
The link you posted seems to show all of your recent comments, the most recent of which is not related to strings. Did you mean to link this? https://news.ycombinator.com/item?id=16546910
>I started learning Rust over the weekends and I think the second edition "The Rust Programming Language" is among the best introductory books on a programming language I have read (well half way so far).
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.)
Depends what you mean by application. So far I only worked on simple exercises and translated some algorithms I have previously written in Python. Compiling them is fairly easy since the compiler output is very helpful and IDE integration is quite good for still being new. Main painpoint is, that I haven't managed to get the debugger work yet.
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 :)
Thanks for the straightforward answer, and the clarifications too.
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.)
I mentioned CRUD web apps because first, I get the feeling that at least a bunch of the comments talking about high learning curve of Rust are from web developers expecting as fast an on-boarding as say in Python, Node.js or Go and more importantly because I'm a web developer myself with a lot of domain knowledge -- building a simple crud web app (or in Rust context more api) is going to be fairly easy to me in pretty much any language that provides some basic tools for it.
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.
Thanks - this was very interesting. Bearing in mind that I only went on what I've heard anecdotally (never tried rust), I was frankly surprised that as someone who has domain experience in web development (in what I thought were "easier" languages) you did not feel frustration at Rust "on-boarding". So you definitely countered what I thought.
I recently was at a two-day university hackathon. The students had never done Rust before. They learned Rust on the first day, and on the second day, built a website, with admin login stuff, etc.
Yeah, I expect it to be an exciting adventure down the rabbit hole starting from my visual Pure Data prototypes. And I have no clue what lurks beneath:)
Obviously, but it's a page about strings and a hint about how to get exact character length would be nice to have. But sure, the answer is just a googling away.
It is true that we could point people to the ecosystem more. Historically, we've been worried about playing favorites. However, this can come into conflict with our principles of having a small stdlib. It's tough.
My sister ordered me a copy of that book on Amazon for my birthday in mid-2016. Then she was surprised that it was a pre-order and I wouldn't get it until October.
I've been spending a good majority of my work hours with Rust and especially writing network services using Tokio. When things work there, it is very reliable, fast and has a tiny footprint with resources.
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.
You've changed my mind on adding special syntax for `async`, generators, and results (`?`). I previously thought it was a big mistake given that these all generalize as monads (granted, there is some ground to be covered before such an abstraction would fit in Rust).
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.)
> (granted, there is some ground to be covered before such an abstraction would fit in Rust)
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 >>=.
HKT would somewhat ease the type signature question; as I understand it in the rust community being able to parametrize over ownership/mutability is the big selling point that gets discussed. You could instead have a single Fn<Foo>, and stuff may require Fn<mut> or Fn<const>, rather than a separate FnMut. There's no keyword now, but owned could be part of that dance too.
There's obviously the question of how to deal with backwards compatibility though.
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.
Algebraic effects are new hotness in FP research. They are easier to compose than monads and they can express things like async/await naturally.
However, I'm not sure this approach works as intended with imperative programs where code will have (side) effects sprinkled everywhere. And there is no GC in rust
I'm a fan of effects, and I think they would work great for Rust. (I/O just probably wouldn't be one of them, or else it would be "on by default" like `Sized`.)
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.
You don't need do-notation to use Monads, that's just useful for building up a lazy-evaluated data-structure describing what IO to perform.
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.
I'm... not sure what you're getting at here. Rust is chock-full of "monadic concepts" like that, including the entire sets of `Iterator` and `Future` combinators. (Note that even without do-notation these have the composability problems I mention- you can't, for example, `break` out of a loop from within a `.then` callback; you have to break the loop down and reimplement it yourself with recursion. This is what the async/await "sugar" helps with- makes normal control structures compose with async code.)
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.
It would be nice to see examples of these new systems hypothetically working together... what does an async DB access within a web request look like with async/await+tokio 0.2?
Pretty much like your good old sync code. Some examples how it might look like you can read from the async/await macro crate [0], somebody might be able to provide more recent examples...
Is that because async is involved? I have minimal experience with async Rust code, but my experience with Rust compiler errors has been superb so far. The compiler always tells me exactly where things went wrong and the error messages are quite informative.
Yes, but also no. That is, async is done via futures, which means that you're composing a lot of futures together, which means that you have a long type signature, which means that if you get a type mismatch, you get quite a long error, where 99% of the type is the same.
Heres just hoping that Mozilla is prepared to take responsibility for what they are trying to accomplish in the long haul.
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.
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.
Is MIR being treated as part of the language definition? Most languages would treat such a thing as an implementation detail, so defining the constraints in that fashion seems like it still looks like it may tie you to the reference implementation.
My maybe-wrong sense was more than the MIR could change from one compiler release to the next, but that the editions couldn't introduce language changes that would require any given release of the compiler to have to produce MIR of a different kind for code of one edition vs. another. You'll be able to compile a single binary relying on different libraries pinned to different editions, and I think the idea is that from the MIR stage of the build onward, whatever differences may exist (e.g., a reserved word in one edition might be a function name in another) have to be papered over so that whole-program optimization/compilation can occur.
I wish all contributors and lead of Rust to have the best collaboration (and hopefully success in solving problems at hand) in the future. It's not perfect, but it's such a nice idea/language.
> 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
What are you talking about? C++ has continually been in the top 5 on the TIOBE list.
I'm loathe to dignify TIOBE with analysis, but even by TIOBE's own numbers C++ has fallen from 15% in 2001-2004, to 10% in 2005-2013, to 5% in 2013-2018. And given that C++ was ranked #2 as early as 1993, even the era of 15% was likely a fall from an earlier height. Compare C, which has held steady since 2001 (with the exception of that huge anomalous dip last year which is being corrected (and which caused C to trigger TIOBE's "language of the year" despite having hugely fallen in TIOBE's own rankings, which helps illustrate how little regard we ought to give TIOBE).
Strange. I have been continuously employed developing in mostly C++ since the 1993 and have only ever seen increasing adoption of the C++ standard library.
Lots of nice language improvements lined up for this year
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!
Not sure if you'll see this, but I really appreciate all the time you spend on here and other places answering questions. It makes a huge difference in getting people interested.
- 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).
OK so non-BC syntactic changes, and warning -> error transitions. Didn't think about the second one but that's way cool.
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.
I assumed that the "edition" thing is just an indirect reference to v1.28 or v1.29, just like some linux distros tag the LTS acronym on long term support releases to differentiate them from subsequent intermediate releases. No jab needed, just a codename to refer to the minor version which will be handled as the most stable release and serve as a line in the sand regarding what represents the go-to version for long term projects.
Rust 1.29 (for example, not committing to a particular release here) will be able to compile both editions worth of code. Rust 1.300 will still be able to compile Rust 2015.
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.
It's less about goals of the technical project, and more about goals of the organization. Rust the language is written by people, The Rust Team, most of whom I'd imagine are volunteers. In order to guarantee the life of the project, its important for potential contributors to not only feel comfortable using the language and writing code, but also feel comfortable in discussing the future potential features and use cases for the language.
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.
There is a difference between "including" and "not excluding" people based on color or whatever. It's (officially, anyway) about removing barriers, not roping people in. I'll grant in practice it's a blurry line.
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.
I completely agree, and I'd like to add that if one is "not excluding", asking for color skin should be forbidden.
Because knowing color skin cannot possibly help one to "not exclude".
In other words, "not excluding" implies not asking for color skins.
Eh, depending on the circumstances it might help you measure how well your anti-exclusion efforts are going. You'd definitely prefer it to be anonymous, and unlinked to any kind of entry process, including informal ones.
This is correct. I mistakenly assumed that if they wanted to know about it, then they wanted to change something according to it, which may not be the case.
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).
then this brings a discussion on what "just" is. Does just implies impartiality? if so, different treatment by color would not be just, from what I can understand.
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..
It upsets me that the reason this joke plays so well is because we can see how this kind of thinking has become common in the world. Take your upvote, but know you've also made me sad. :/
If you're open to learning about why it _actually is_ important, there are some good resources out there to give you a little more context as to the reasons why.
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.
What if it happens that you can only hire one person,
and one "externally looks" homogeneous to the rest of the hired workers, but the other one "externally looks" like a minority member,
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?
> 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.
There are many axes along which a community can be non-inclusive. I will always remember being made fun of in #gentoo for using Haskell. (Although I can not argue with the general guidance to install Gentoo.)
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.
Its all just fluff. Might have something to do with Mozilla being left-leaning.
Skin color has nothing to do with non-lexical lifetimes.
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.
The only fluff I’m seeing here is your nonsense about about “taboo” or “uncomfortable truths”, which are usually a hallmark or someone with nudge-nudge-wink-wink dogwhistle objectionable opinion.
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.
> 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.
> It means you have already spewed your privilege in believing that the diverse opinions are already bad/dumb ones.
this sounds uncalled for in my opinion. there is a difference between saying that something is "needed" and that something is "good". The parent simply said that the statement on the need of diversity was likely political.
Diversity is something worth investing into, many agree with this, but to saying that a project needs it implies that you cannot succeed (whatever your idea of success is) without it.
EDIT: I am totally ok with a project choosing to support diversity both within itself and in the broader community, i believe that many ways of doing it are extremely beneficial. yet often that _is_ a political stance and can be discussed as one.
> but to saying that a project needs it implies that you cannot succeed (whatever your idea of success is) without it.
Sure. I think a lot of people are also defining succeed as "be somewhere or something I still want to associate with in the future."
There have been a few high profile projects that have gotten themselves into hot water because of either unwanted behavior by some that wasn't corrected (or they didn't have a good policy to point to for correction) or was too strongly corrected (different people have different thresholds, and without a clear concept to point to, even if somewhat vaguely defined, some people tend to go nuclear before a warning shot has been fired, which is also unfair).
We may at some time get to the point where communities are including too many general and positive policies, but I don't think we're there yet. In the meantime, when correctly followed they provide help and protection to people on both sides of whatever issue they are concerning.
Something is "needed". If you don't create "political" change in a world where nothing otherwise would transform, then what? If we don't "force" schools to be diverse a class of people never excel and Obama would never be president.
All projects need it, because that project is made of humans. That project can succeed and die but those individuals will carry those experiences forward. I get what everyone here is doing linguistic math with diversity, but none of the replies are taking the impact on humanity in parallel with the success of a business that humans create.
We are reaping the benefits from many political changes 50+ years ago that inspired us to see the world differently and we cannot and should not stop now.
Okay but what does one actually do to make the community more 'diverse and welcoming'? What is preventing a particular person from contributing to the project?
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.
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.
This works quite well, until you're dealing with stuff such as `AndThen` with 10 different nested futures. Here, again, async/await will be a great help.
Nope, I'll try that out tomorrow, thanks for the advice!
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!
Can't agree more!
Through i am afraid there hugh work to be done before i can use it at work since i find we are increasingly rely on vendor's sdks to workaround hardware limit and slicon errors
Okay, after two failed attempts (mainly due to lack of motivation, I hasten to admit), I think it's time for me to learn me some Rust.
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)!
I've had a couple false starts over the past 2 years or so as well. This time I'm determined to reach "critical mass" where I'm comfortable reaching for Rust when I need to hack together a new CLI tool, web service, whatever. I won't quit this time if you don't.
In the end, all these words are similar enough that we had to make a call.
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.
Era, epoch, edition, excretion, excrescence,... I'm looking forward to watching Rust's continued evolution in the new... whatever they decide to call it.
For me epoch is more readily associated with new version new features, whereas edition is more like format, such as Mobile edition, Tablet edition, etc.
Could this be used to add an "Ord::clamp" or similar to the stdlib such that it's invisible (and thus wouldn't cause any conflict) to crates with an older "edition"?
I think for such things it's preferable to just have a `clamp` method as a static method, i.e. you call it with `Ord::clamp(foo, bar)` instead of `foo.clamp(bar)`.
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.
As someone who does a lot of firmware I think I really need to explore more of what Rust brings to the table there. Interrupts are a major source of concurrency and I'd like to be fearless about those issues.
I'm not experienced in this space, but you might be interested in the Real-Time For the Masses[1] project. The author of that is the lead of the embedded devices working group.
Here's my wishlist for embedded: first class hierarchical state machines, saturated arithmetic, fixed point types. In bare metal scenarios, these are much larger areas of potential errors than any ownership issues, since often, any dynamic allocation is restricted to a mailbox/queue primitive while everything else is statically allocated.
What I'm missing here is why we need to market it as a distinct Rust 2018 release. Do we have breaking changes that require a new epoch/edition? The "Language improvements" section doesn't clarify that. I just hope there is substance in this push, a real technical reason for a new release, not a marketing one.
There are changes that require a new edition, particularly around new keywords.
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.
That's what I've been afraid of. When epochs were announced, I thought it is kinda nice to have backwards compatibility while introducing breaking changes. It's a good technical concept. Trying to change it's role into "marketing/communication/project management tool" out of a sudden is what puzzles me.
> 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 :)
> 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.
> That's what I've been afraid of. When epochs were announced, I thought it is kinda nice to have backwards compatibility while introducing breaking changes.
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
Marketing reasons are just as "real" as technical reasons.
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.
I'm not just being diplomatic when I say that hopefully we can avoid the Rust vs. Go debate here. :P WASM is a bid to help future-proof the relevancy of the web in the face of mobile app stores, and seeing many languages embrace it is a good thing, no matter what those languages are. Heck, since WASM is sandboxed anyway it doesn't even matter if you choose C or C++ over Go or Rust, since memory unsafety shouldn't be exploitable from WASM anyway.
Well, it's not exploitable to get out of the sandbox, but there's still interesting data inside the sandbox. For instance, if some website's wasm is parsing the URL to read the URL fragment or something, you could imagine an attacker sending me to a malformed URL, hoping to overflow the URL parser and gain execution with all the cookies of that web page.
True, I suppose I should temper down "shouldn't be exploitable" to "shouldn't be able to provide a stepping stone to system-wide arbitrary code execution".
I think the domains of use will be quite different. Since Rust doesn't require a GC or other runtime support, we envision it being used in specific modules to provide a boost to code otherwise written in JS. See https://hacks.mozilla.org/2018/01/oxidizing-source-maps-with... for example.
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.
Let me throw Nim into the mix and say that it has had a JS backend for years now. It's officially supported so it's very stable.
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).
To be clear, Rust has also had support for asm.js and wasm via emscripten for quite a while. We're very enthusiastic about the new LLVM backend though, as it allows for significantly more control, including file size.
> (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.
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?
It's not at all easier to interoperate two separate GC'd languages unless you have one garbage collector to manage them both. Web Assembly does not provide the hooks necessary to integrate GC, and JavaScript does not provide critical features, such as finalizers, that are necessary to do things like break cross-language cycles. Absent that support, manual memory management of DOM objects is necessary, and Rust is going to have a much easier time of that than Go will.
For specific needs I have occasionally made my Go struct constructors (NewFoobar() *Foobar) use malloc ported to Go + mmap to allocate memory and said structs have a .Close() method (think defer foobar.Close()). It worked fine... This is a fine solution/compromise, in 95+% of development solutions you don't want your devs wasting their time managing memory manually, and in the worse cases they will get it wrong.
GC's have some kind of inherit reference counting which makes releasing pointers safe, in any direction. You can't do that with C/C++ without something like glib.
Not entirely sure what you are referring to or how it relates to the issue here. If you have two separate heaps (what you have between go and JS) then they will be collected separately. As such you need to somehow keep objects on the other side alive. Worst: in Go the language does not guarantee that the GC won't compact (it doesn't do correctly though). As such you can't even pass pointers to JavaScript, you need to create handles.
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.
> GC's have some kind of inherit reference counting which makes releasing pointers safe, in any direction.
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).
If you have reference counting across GC boundaries, you can easily create memory leaks because of reference cycles, e.g. GC 1 cannot release object 1 because it references object 2, and vice versa. (Unless you only allow refs to go one way.)
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.
No major JS implementation uses naive reference counting, because there's no way to break cycles. Cycle collection requires the ability to trace the heap globally. But the JS engine does not have any way to see inside a wasm heap.
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.
All those iterator types are exactly the same as C++ having std::vector::iterator, std::vector::const_iterator, std::deque::iterator, ..., and exist for similar reasons: mostly zero-overhead abstractions. (This is even more visible in the range proposals for C++.)
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.
The design of const generics is fine, but there's underlying technical work in the compiler to be done. We expect const generics to land in nightly this year, but they're unlikely to make it to stable before 2019.
As far I understand the RFC won't allow for `Array[Y, N]` (or `Array<T, N>` in Rust parlance) note eddyb's comment:
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`.
One of the biggest issues with looking at tracking issues for me is that it's impossible to judge either priority or complexity. This one for instance is a good one. Tags say implemented and approved, the checklist is mostly docs. Yet it looks like from the discussion that this issue is closer to not landing at all in this form than landing.
As with many large features, people with their heads down will be working on it for a long time without much fanfare, until suddenly the feature appears in a mostly-usable state shortly before it is able to be stabilized. Not much solace for the people who want to be able to predict when features will land, though the tracking issue should at least give some idea as to which developers are prioritizing this so that one can contact them directly for more information.
Webassembly (and JavaScript) are single-threaded. The platform has web workers, but they are more like processes since they mostly don't share data.
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.
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.