Announcing Rust 1.0 Beta
blog.rust-lang.org
blog.rust-lang.org
> the beta release represents an accurate preview of what Rust 1.0 will include.
and > We don’t plan on making functional changes to stable content,
> though naturally we may make minor corrections or additions to the
> library APIs if shortcomings or problems are uncovered (but the bar
> for such changes is relatively high).
This is the first release we've promised this kind of stability. The team and community has been doing an incredible amount of work lately to make this happen. We really hope you'll like what we've been up to, but Rust isn't for everyone, so no worries if it's not for you. Constructive criticism always welcome. <3As of today, my job gets harder: "things are always changing" is no longer an excuse for missing docs. The six weeks until release is largely about polish, and I have some pretty big plans...
If you'd like to help out, one of the best things that new users can do is simply to write a toy project or library in Rust and record their experience. If you crash the compiler somehow, file a bug! If you're confused by a poorly-worded error message, that's a bug too! If our docs are lacking, heck yes that is also a bug! If we're missing some crucial API on the stable branch, let us know so that we can prioritize which APIs to stabilize! We care about every aspect of the experience of developing Rust, and though our resources are finite we'll do our best to address the most common pain points first.
If so, what kind of research did you do to make Rust usable? Any surveys or anything like that?
I'm genuinely curious because, as far as I know, none of the major languages were designed to be usable (which is not the same as "concise" or "easy"). Some of them have made huge strides in usability, but (understandably) none of them started that way.
Usability involves more than syntax --package management, interoperability, docs, and error messages. Since you mentioned those things, it made me wonder whether Rust were designed in a more user-focused way.
the primary focus has always been systems-language speed and memory safety without a gc.
That said, my personal opinion is that usability is absolutely a primary focus of Rust. The notion of "regions" (renamed to "lifetimes" in Rust to be more user-friendly) has existed in academic literature for a while, but Rust has spent an enormous amount of effort and tried a wide variety of approaches in its quest to make this concept as accessible as possible to the everyday programmer. In other words, Rust is a research project focused on advancing the usability of safe and zero-overhead memory management.
I wouldn't say it is the primary focus, because first achieving the task of safe and zero-overhead memory management is itself a major undertaking.
Getting feedback on usability hasn't generally been achieved by formal surveys, but instead generally by brainstorming possible changes to the language and taking part in the resulting discussion. These discussions have happened in a variety of forums over the years so I can't point you to any comprehensive collection of them, but very often the ideas themselves are initially presented on the blog of Niko Matsakis, who is currently Rust's technical lead ( http://smallcultfollowing.com/babysteps/blog/categories/rust... , which is a fascinating read for those looking to understand the language's volatile history). It also involves being open to proposals from people using the language in the community, which eventually turned into the current RFC process ( https://github.com/rust-lang/rfcs ) and which is a bit like Python's PEP process.
> Usability involves more than syntax --package
> management, interoperability, docs, and error messages.
Rust's first stab at a package manager (the original namesake for the current Cargo) was in 2011, and we had at least two others in between. The fact that Mozilla was eventually willing to contract out development of the current package manager to domain experts (the Bundler developers) should show how much we care about having a good solution to this problem early-on. It has also been a goal from the beginning to have a rock-solid and very transparent means of interoperating with C, and being able to expose Rust libraries as C libraries was also something that was desired from the outset even if it took a while for the design and implementation to materialize. Having good error messages has always been taken very seriously, and I'm happy to see that people praise us very often for the niceness of our compiler messages (though we can still be doing better in many places). As for docs, Mozilla's been contracting Steve Klabnik for over a year now to not only write a comprehensive tutorial for Rust (now a book: http://doc.rust-lang.org/nightly/book/ ), but also to write up documentation and usage examples for every single type and function in the standard library. I'm genuinely curious because, as far as I know, none of the
major languages were designed to be usable...
I think that's a little narrow-minded. Of course languages are designed to be usable. "Usable" is almost meaningless on its own, as every language is intended for use by developers to solve problems (toy languages notwithstanding).As for your specific goals, "package management, interoperability, docs, and error messages," I think there are in fact major languages designed with very specific goals in those areas. Node.js for example was designed to leverage the immense popularity and familiarity with JavaScript, and it has a clear story for package management that has enabled an enormous ecosystem. Go is a language that from the get-go was designed with a coherent story of how to build large and maintainable systems, encompassing package management and the module system, language features chosen for their simplicity such as interfaces and the lack of generics, and a canonical source formatting tool to attempt to make the whole ecosystem more readable and understandable.
Rust of course has its own answers for usability. They too have a package system that can enable a thriving ecosystem. Their approach to memory management is a naked attempt to make their language "usable," an attempt to make programming more user-friendly language in contexts where interpreted languages, bytecode and even garbage collection are untenable. Their docs are coming along nicely, their error messages perhaps less so.
To say that no major languages are designed for usability is laughable. In fact, I would say that every major language since and including x86 machine code has been designed with usability concerns front and center. The more recent wave of languages especially -- practically no one is trying to beat C and C++ on technical capabilities. They are trying to beat them on ergonomics.
I'd say the major languages are C, C++, C#, Java, JavaScript, Objective-C, PHP, Python, and Visual Basic. I wasn't even thinking of Ruby in that list, and I definitely wasn't including the new kids on the block (Rust, Go, Elixir, etc.) I'm not trying to get into a huge argument about what is a major language -- this is just a rough list that I had in my head and not something I'm set on.
Of those, the newest (PHP, JavaScript, and Ruby) are now 20 years old.
I'm certain that none those came with the important usability features we think of now. I'd also like to think that the science around programming has improved in 20+ years, and we're designing better languages in the 2010s than we were in the 1990s.
Again, this isn't just about syntax or the standard library. The docs and tools around the languages above are mature now, but they didn't start out that way. Rust is interesting because, unlike any of those languages, it's trying to start out with the mature docs and tools we expect in 2015.
And although I can't speak for them, everything I've read suggests that Go was not primarily designed to be usable. The creators intentionally left things out (generics are the obvious example) in their attempt to favor simplicity over usability. I'm not saying it's a bad choice, but it is a polarizing one.
In fact, if you search for criticism of Go, that's going to be near the top.
Another is that the packages don't have version-matching.
Edit: And, again, this is not to say Go is a bad language or that people shouldn't use it. I'm just pointing out one example of where the Go authors prioritized simplicity over usability. I'm not commenting on the whole language.
But your last few comments in this thread seem to have taken a different tact. You've projected some definition of usability that you hold instead of acknowledging that others may think differently. For examples, I can promise you that the Go authors don't think they "prioritized simplicity over usability." They would say that Go is very usable because it is simple.
You disagree, and that's totally cool, but we should be careful to frame it as a difference in perspective.
P.S. Rust and Go are very usable (to me). :-)
I've also acknowledged that even my definition of "simplicity" isn't the same as everyone else's.
You seem to be reacting to my argument that boilerplate != usability, and I stand by that. Typing the same thing over and over is fragile, and it's antithetical to the concept of programming. If there's anything that I can say with absolute certainty is vital to usable programming, it's DRY.
Other than that, I really am curious to know what usability really means. I've seen a lot of interesting studies on HN that contradict commonly held notions (e.g. refactor often). It'd be so cool and important for a language to base itself entirely off of such research, rather than a small group's experiences.
Usability just means ease of use and learnability. In context of computers languages, I think having a smaller set of idioms/stuff to learn is more vital to usability than to have less stuff to type.
I don't understand them, but they say those words.
I'm not sure if I agree philosophically. If I do, then I'm not sure I like that kind of simplicity for writing software. I'd rather have slightly DRYer code than slightly simpler code.
Not really. That path leads to a Forth, a Scheme, a Smalltalk.
There are plenty of things which are not necessary in Go. Many of them are eminently convenient, but could be libraried with generics: most of the built-in magically generic-even-though-the-language-doesn't-have-generic collections for a start. the special-cased multiple return values, the magical builtin functions (which generally exist as support for the aforementioned magical collections).
I've read it before, Go is not simple, it's simplistic. A simple language would by necessity give a lot of power to the developer, that's not what Go does.
There's a difficult balance to strike of course, get too simple and you end up with turing tarpit, and simple languages may make it harder to create cohesive communities. Still, calling Go simple is an insult to simple languages.
> To help ensure that we don’t accidentally introduce breakage as we add new features, we’ve also been working on an exciting new CI infrastructure to allow us to monitor which packages are building with the Nightly builds and detect regressions across the entire Rust ecosystem, not just our own test base.
Rust is very exciting.
It has gotten many things right -- a really good approach to safety, didn't sacrifice performance, powerful well thought out type system. I think we might finally see the next generation "systems language". We've heard promises before but hopefully this is finally it.
let path = Path::new(&app_path)
println!("file name: {}", path.file_name().unwrap().to_str().unwrap());
Hopefully this is just a case of unfamiliarity, but surely something like should be easier?On the contrary, the lack of the string/string-view distinction is widely considered a flaw in the C++ standard libraries. C++14 introduces std::string_view to correct it. Rust's String/str distinction is just the same as C++14's std::string/std::string_view.
Then "types start with an uppercase letter" except when the language creators break their own rule.
Then the fun "sometimes we use () and sometimes we use [] to do practically the same thing".
Then the various insanities with ::.
Then Generics. How many examples of languages which tried to use <> for Generics do we actually need before people learn the lesson?
I really wished some people took Rust, and wrote a new frontend to get rid of all the pointless inconsistencies and other "C did, so it has to be good"-isms (like_all_this_underscore_stuff or the ridiculous abbreviations), because many ideas of Rust are not only good but brilliant and deserve to be put into languages which don't belong to the purely-academic category.
I really wish Rust to succeed, but can we stop this approach of "3 steps forward and 2 steps backward" to language design?
Do you have an example?
> Then "types start with an uppercase letter" except when the language creators break their own rule.
They always start with a capital letter, except for built-in types or FFI bindings to libraries such as libc where the original types used a lowercase letter. This convention is exactly the same as Java.
> Then the fun "sometimes we use () and sometimes we use [] to do practically the same thing".
I've never heard this before. Do you have an example? () is used for function calls, grouping, and tuples, while [] is used for array literals and array indexing.
> Then the various insanities with ::.
The double-colon is very useful so that you can tell the difference between module lookup and field projection/method calling. Early Rust used . for module lookup, and it was confusing to tell at a glance whether a function was being called or a method was being invoked.
> Then Generics. How many examples of languages which tried to use <> for Generics do we actually need before people learn the lesson?
Using square brackets like Scala wouldn't actually help with the ambiguity, because it would be ambiguous with array indexing. The only syntax I've seen that actually solves the ambiguity is D's `!()`, which was deemed too unfamiliar. Angle brackets were chosen because they are familiar and aren't really any worse than the other mainstream options.
> I really wished some people took Rust, and wrote a new frontend to get rid of all the pointless inconsistencies and other "C did, so it has to be good"-isms (like_all_this_underscore_stuff or the ridiculous abbreviations)
The underscore naming convention was actually taken from Python's PEP 8. Rust doesn't really have any more abbreviations than most other languages at this point.
> I've never heard this before. Do you have an example? () is used for function calls, grouping, and tuples, while [] is used for array literals and array indexing.
Maybe referring to the ability to use either type of brace with macros?
Array indexing is a function call, because an array is a function from its index set to the set of the values the array stores, or -- equivalent -- a partial function from integers to the value set.
Scala gets this right.
I don't know, I never liked how all FunctionNs are sort of "unofficial" partial functions (because they may throw), and PartialFunctions are then made a subtype of FunctionN, because they have an extra method where you can ask whether they are defined at given points. Especially since throwing seems to be falling out of favor in Scala, being replaced by Try[] return types.
You wrote that array indexing is "a partial function from integers to the value set". If we're to take the suggestion seriously, then the practicality of it depends on how partial functions are represented in the language. Which itself is certainly not orthogonal to the Try-vs-exceptions issue.
I was talking about the mechanism for array indexing. In both cases, indexing is function application. So the choice of error representation is orthogonal. This is even more evidence that we should not use a different operator for array indexing as Rust does alas.
This is exactly when you want a systems language to require explicit conversions, rather than converting things silently and possibly losing or corrupting data.
Exactly. Python3 went down the "silently converting" route, and it's not pretty[1]. I would go so far as to call it harmful.
http://lucumr.pocoo.org/2014/5/12/everything-about-unicode/
I understand the difficulty in this space; much of it is caused by forcing the Windows unicode filesystem API onto python as its world-view, rather than sticking to the traditional Unix bytes world-view. I'm unixy, so I'm completely biased, but I think adopting the Windows approach is fundamentally broken.
import sys
import shutil
for filename in sys.argv[1:]:
if filename != '-':
try:
f = open(filename, 'rb')
except IOError as err:
msg = 'cat.py: {}: {}\n'.format(filename, err)
sys.stderr.buffer.write(msg.encode('utf8', 'surrogateescape'))
continue
else:
f = sys.stdin.buffer
with f:
shutil.copyfileobj(f, sys.stdout.buffer)
Python's surrogateescape'd strings aren't the best solution, but I personally believe that treating unicode output streams as binary ones is even worse. let path = Path::new(&app_path);
println!("file name: {}", path.display()); > The design of a path abstraction is surprisingly hard. Paths work
> radically differently on different platforms, so providing a
> cross-platform abstraction is challenging. On some platforms, paths
> are not required to be in Unicode, posing ergonomic and semantic
> difficulties for a Rust API. These difficulties are compounded if one
> also tries to provide efficient path manipulation that does not, for
> example, require extraneous copying. And, of course, the API should
> be easy and pleasant to use.
So, in this case, you have to call `file_name` to get the filename, but that returns an `Option`, since it might not be a file, it could be a directory. The `unwrap` says "crash if it's not a filename." You then have an `OsStr`, which is a string type that handles the kinds of cross-platform issues alluded to in the above paragraph. `to_str` will then attempt to turn it into a `&str`, but because `&str` must be unicode, if the conversion fails, it _also_ returns an Option, which is the last unwrap.In systems programming, details matter. As such, our stdlib APIs try to expose those details. I expect some people will write higher-level abstractions that hide all this if you don't care as much.
Tradeoffs tradeoffs tradeoffs...
I'm not convinced it's being a "systems language" (in the sense I think you mean). The last three languages I've learned to fluency have all, in various ways, been very concerned about errors: Erlang, Haskell, and Go. Yes, they have widely varying opinions about what to do with them, but they all agree that you have to be fairly constantly worried about them. You can't sweep them under the rug; you need a strategy, and you need to deal with the resulting issues head on.
Despite their differences, what I feel like I've learned from them is that historically speaking, the vast majority of popular languages and their communities have been downright cavalier about errors. It's often in all the little details rather than a consistent, stated design goal, but the presence of various nils and nulls, APIs that will happily return empty strings for paths instead of errors, and all kinds of APIs and libraries that seem to be subconsciously written with the belief that returning an error is a failure on the part of the library and will go to what now seem to me to be absurd lengths to somehow keep klunking along in the presence of already-manifested failure. Even for languages that think they're being really good with exceptions, I find that when I go back to them, they end up using the exception support to "permit" themselves to be sloppy and hope the exceptions will pick up the pieces... and they often don't. Not because of some theoretical weakness, but because allowing developers to think they don't have to think about the error cases means precisely that they don't!
People are used to errors not poking them in the face until deploy time. It seems to me there's a strong, broad motion towards developing languages that will in one way or another force you to deal with them at compile time. It's a culture shock for people, but, I'll tell you this, get really good at Rust or any of these other languages and go back to any of those other languages, and you will find your code changes in those other languages to suddenly look a lot more like your Rust does anyhow, which means this putative disadvantage of Rust evaporates anyhow. Once you see how sloppy you were being, it's hard to go back in good conscience.
Yeah, maybe I should revise my statement a bit: systems languages, overall, tend to expose more details. I meant this in a broader context than errors. As a simple example, manual memory management exposes more details than GC. There are many languages that care dearly about errors, as you've rightfully pointed out.
I broadly agree with the rest of your post. Error handling is hard and tedious, and programmers don't like doing it. I myself had an embarrassing bug in `rustbook` where I did `let _ =`. I only intended to start off that way! Programming is hard. But using Haskell, Rust, and other languages like this have certainly changed my code in other languages.
> libraries that seem to be subconsciously written with the belief that returning an error is a failure on the part of the library and will go to what now seem to me to be absurd lengths to somehow keep klunking along in the presence of already-manifested failure.
It's a big problem with hairy repercussions if this idea is allowed to take root.
println!("file name: {:?}", path.file_name());
or just for escape characters, but only if the file name existed: if let Some(name) = path.file_name() {
println!("file name: {:?}", name);
}
Note that the unwrap() isn't boilerplate, in either case: if you are using `unwrap()` you are implicitly saying that you know for a fact that the file name is present (the first time) and a valid UTF-8 string (the second time), which isn't guaranteed in the general case on all platforms. If you wanted to write this more robustly, and these weren't guaranteed, you would need to explicitly take care of those cases.I/O has lots of tricky error cases, and Rust usually requires you to acknowledge that. You can avoid dealing with those cases with unwrap(), or you can explicitly handle them. Personally, I much prefer this to being surprised only when the weird error case unexpectedly turns up because I never considered the situation in question, and that's generally how Rust handles things, but it depends on the task. It may make Rust less appropriate for quick scripting tasks, for example, where the domain is well-defined and the script is not likely to be reused (but I find that those scripts have an annoying way of getting reused anyway).
This is sort of rust's philosophy; being explicit. Rust has no problem with failing, it just has a problem with failing without you telling it to. If you write a wrapper that implies you are willing to fail on these cases, then you're being explicit by choosing to use these functions instead :)
path.file_name().and_then(|f| f.to_str()).unwrap()
but that really doesn't buy you much.Where familiarity comes in I believe is in what that line communicates to me: There might not be a file name (like having '/' as path), and that getting a string slice out of it might fail (the kind of string the platform uses for paths might not be UTF-8 compatible).
Plus, the unwrap()s tell me that whoever wrote the code considered it an assertion failure if one of those is happening.
I read a lot more Rust than I write, and it really helps that the code communicates a lot. And with knowing about OsString/OsStr beforehand, I only had to lookup when file_name() returns None, the rest is rather implicit by convention, or familiarity.
You can convert an Option to a zero-cost iterator and then use `flat_map` though.
This leads to verbosity at times, but usually its a good verbosity.
I presume there is (or could be) a standard lint to prevent this getting into production code, but it still feels kind of icky.
That would be nice, but as others hinted that wouldn't actually work:
* there are paths without a file_name (`/`) so it returns an Option to make that evident
* to_str attempts to convert the OS-dependent thing to a Rust string. Rust strings are valid unicode, but many filesystems don't have such strict requirements[0]. Again Rust returns an Option to makes that evident although there's a "to_string_lossy" method which will simply replace invalid sequences with U+FFFD if you don't really care
The Rust philosophy is generally to expose these issues upfront and force the developer to consider them (and either handle or explicitly ignore them). It is indubitably less convenient than e.g. Python 3's `PurePath(app_path).name` returning an str, but you'll have had to decide what happens in case of failure.
Paths are really a good example of something which looks simple, should be simple, and is actually anything but simple for historical and technical reasons, especially in a cross-platform context.
[0]
* Unix paths are the most troublesome: they're arbitrary bags of (non-NUL) bytes, they don't have to be in a known encoding, they don't have to be in any encoding.
* Windows paths are not guaranteed to be Unicode either: they're UTF-16 code units aka UCS2 + surrogates, and the surrogates may be unpaired, leading to invalid Unicode.
* OSX paths are guaranteed to be valid Unicode.
let path = Path::new(&app_path);
if let Some(ostr) = path.file_name() {
if let Some(str) = ostr.to_str() {
println!("file name : {}", str);
} else {
println!("WARNING : file name is not a valid unicode sequence ! (file name : {:?}", ostr);
}
} else {
println!("Path is either a root directory or a dot entry, it has no filename");
}I'm reading the docs, and they say that for I/O, use the 'old_io' module, which is...deprecated?
If I were to start doing any I/O in Rust beta, would that code be essentially throw-away as soon as the 'new' io module lands?
"Common types of I/O, including files, TCP, UDP, pipes, Unix domain sockets, timers, and process spawning, are defined in the old_io module."
Following the "old_io" link leads me to http://doc.rust-lang.org/nightly/std/old_io/ which says "don't use me"
Same with libcollections/fmt.rs defining the write! macro as taking an old_io::Writer
There's also a bunch of old_io in http://doc.rust-lang.org/book/concurrency.html
Still building beta, but shouldn't this work? https://github.com/rust-lang/rust/pull/24024
One of the hardest things about learning Rust right now is that because it is not 1.0, lots of the tutorials, sample code and 3rd-party documentation is outdated - or parts of it are.
Learning Rust "the right way" will certainly get easier after 1.0 is finally out and reliable/stable the tutorials, code bases and doc's will follow.
[1] I just checked and it is in fact updated.
Is there a documentation on how to build single binary executable for Rust?
Right now, we depend on glibc, and it cannot be statically linked. By default, Rust applications statically link everything but glibc already.
At least one core team member is strongly advocating for musl support for truly standalone binaries in the 1.1 timeframe, but nothing is likely to change for 1.0, and it's too early to decide on 1.1 features yet :)
> Right now, we depend on glibc, and it cannot be statically linked.
Could you expand on this? Why can it not be statically linked?[1]: https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked...
[2]: http://stackoverflow.com/questions/3430400/linux-static-link...
Does rust currently expose any way to do this? Could I perform linking myself as a workaround?
You can but it requires lots of black magic, and certainly a nightly rather than beta Rust ;)
http://mainisusuallyafunction.blogspot.com/2015/01/151-byte-... is an old blog post that can get you started.
Most people who focus on 100% statically linking just use a different libc. For example, Stali Linux, [1] has this to say: http://sta.li/faq
> it [is] nearly impossible to use glibc for static linking in non-trivial programs.
That said, if you want to give it a shot, it's technically possible, though we don't expose it for Rust yet, due to these kinds of landmines.
1: which got linked here a few times, most recently https://news.ycombinator.com/item?id=7261559
If you want to re-use any of the huge number of mature C libraries out there, you will need to link against glibc anyway.
Directly invoking system calls is quite the violation of the UNIX philosophy :)
Of course reusing C library means linking with libc and reusing C++ library means linking with libstdc++. But in a long perspective those libraries should be implemented with Rust to have all advantages of the better language.
And using rust without libc might help with some embedded use-cases, where people don't want to put unnecessary stuff. Rust as a 0-cost systems language theoretically could be used there instead of C.
I really feel the community, which is already awesome, will start to build new things at a rapid pace.
:(