Hacker News new | past | comments | ask | show | jobs | submit login
Announcing Rust 1.0 Beta (rust-lang.org)
348 points by steveklabnik on Apr 3, 2015 | hide | past | web | favorite | 114 comments



Two big things:

    > 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. <3

As 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...


Can't wait to play around with this!


Six weeks to 1.0-stable! And it's going to be a hell of a ride, since there's still tons of polish to do with respect to tooling and documentation and bugfixes. In other words, no time to celebrate. :P

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.

https://github.com/rust-lang/rust/issues


So would you say that usability has been a primary focus of Rust from the beginning? Is it the primary focus?

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.


    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 wouldn't be so dismissive. For starters, we seem to have different definitions for "major" and "usable".

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.


They say generics aren't usable because they aren't simple.

I don't understand them, but they say those words.


The creators of Go seem to think that simplicity is achieved by removing everything that isn't totally necessary.

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.


> The creators of Go seem to think that simplicity is achieved by removing everything that isn't totally necessary.

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.


I'd guess that many of the worst parts of PHP, and some of the best, were introduced in order to make it more usable.


If Go isn't geared towards usability, I don't think it is same definition of usability most adhere to.


Having to write a ton of boilerplate is not "usable". There are several things in Go that I've never seen written in a way that doesn't use a lot of boilerplate.

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.


You started off this comment thread by asking what kind of research was done to achieve something that is usable. I like that tact because it implies that "usability" isn't something you can reason about from first principles and come up with the right answer to, "Is this software usable?" Instead, it acknowledges the fact that usability should be data driven. What do people using it think?

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 can see where you're coming from, but I've posted a lot of comments on this topic, and I absolutely acknowledge that I don't feel certain about these things. For example, I'm not certain that I agree with Go authors that "simplicity = usability", but I really don't know. I plan to master Go, and then maybe I'll have a better idea.

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.


Something being usable doesn't imply boilerplate being present or not. In fact I think most if not all languages have boilerplate. They might mitigate it with some macros, but the boilerplate is there.

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.


they've definitely had feedback from all the crate creators and actual daily work through - at minimum - Servo's implementation.

the primary focus has always been systems-language speed and memory safety without a gc.


Actually the no-GC thing is pretty recent. Memory safety was the big deal, and IIRC they didn't think it could be done without a GC until sometime in the last couple years.


Ancient Rust accounted for the existence of garbage collection, but still tried to prefer zero-overhead memory management via linear types and move semantics. You are correct that it wasn't evident until much later that it would be possible to get by without GC entirely.


It seems like safety/correctness in general and not just memory safety is a major focus.


I should first disclaim that I'm not a member of the Rust core team, but I have been closely following and contributing to the language since 2011 and so have rather extensive insight into the arc of its evolution.

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 impressed with how thoughtful the entire Rust development cycle has been. They've managed not just to strike a balance between stability and language redesign, but to approach to API and syntax standardization in a way that made tracking the changes relatively painless. Some of this is done in the language itself, with opt-in statements and stability annotations in the API, but the tooling around compatibility in the ecosystem is quite useful as well. Now they're going to doing CI on their own nightlies against packages in the ecosystem:

> 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.


CI against package ecosystems is a really great idea. We do it for Julia too [1], and it can identify some really subtle issues that would otherwise take longer to become apparent.

http://pkg.julialang.org/pulse.html


I'd imagine that such a bank of tests would be helpful in tuning performance too. Though perhaps the reality of each tweak only contributing a fraction of a percent would be considered depressing compared with the standard practice of saying that a microbenchmark is now 2x or 4x faster.


Great work everyone involved!

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.


I like Rust, and I'm probably missing something, but do I feel that some things should be easier. At times the language (or standard library, I suppose) feels a bit unwieldy. For example, trying to coerce a string from certain types:

    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?


If you were willing to allow for potential escape characters and acknowledge (to the user) the fact that the path might not have a file name, you could write:

    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).


However, rust would be relatively well-suited for a "dirty quick scripting task". You could make a more traditional wrapper for the operations you like to use that assumes these rare edge cases don't happen, and panic otherwise.

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 :)


Yes, it's true. Code like this is going to be more verbose. Often, it's due to Rust being a systems language and exposing low-level details to you, combined with the type system ensuring safety. In this instance, `Path` is actually fairly complex. From the RFC: https://github.com/rust-lang/rfcs/blob/master/text/0474-path...

        > 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...


"Often, it's due to Rust being a systems language and exposing low-level details to you, combined with the type system ensuring safety."

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.


Great comment. I have run into this problem exactly in inherited codebases at my last couple of jobs:

> 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.


> The last three languages I've learned to fluency have all, in various ways, been very concerned about errors

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.


> surely something like should be easier?

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.


Also, OSX applies unicode normalization to the filenames, which can cause some interoperability problems with unix, e.g. you try to copy files forth and back between the systems and end up with two copies of a file, with identical-looking filenames that differ in their unicode normal form.


To add to the other comments, Rust prefers to make error cases explicit. We have memory safety. You can opt out of it, but you have to opt out explicitly. We have exhaustive match. You can use a wildcard, but you need to explicitly do so. We have nullables and Results. You need to explicitly acknowledge the error/null case, or use `unwrap()` to say that you don't mind a panic at this point.

This leads to verbosity at times, but usually its a good verbosity.


I'm sure I'm not the first (or the hundredth) to say this, but `unwrap` has always struck me as a horribly innocuous spelling of "yes, feel free to crash here".

I presume there is (or could be) a standard lint to prevent this getting into production code, but it still feels kind of icky.


We almost changed it to `assert` but that made some people _really_ upset.


Dammit, I kept wondering why it wasn't named unwrap_or_panic/unwrap_or_assert but it could be worse at least unwrap is a less friendly name then scala's get.


`.expect()` is slightly better (you can give it a string that will become a part of the panic message)


Just my 2cs, but the complexity of this code is here to remind you there are a lot of cases you have to take into account :

    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 would say it is familiarity, but in another sense. You can use something like

    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.


I wish `and_then` were named `flat_map` so badly.


We have `flat_map` for iterators. (`and_then` works for Options, which are basically nullables)

You can convert an Option to a zero-cost iterator and then use `flat_map` though.


I think unwrap calls are mainly meant for quick and dirty stuff and if you find yourself doing it it may be the wrong solution.


That would be:

    let path = Path::new(&app_path);
    println!("file name: {}", path.display());


No, I just want the file name.


The 'String', '~str' dichotomy is really unfortunate.


Presumably you mean String/&str? (~str hasn't existed for a long time.)

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.


Apart from the Rust's usual rule to invent some abbreviations, but only apply them half the time.

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?


> Apart from the Rust's usual rule to invent some abbreviations, but only apply them half the time.

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.


> > 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.

Maybe referring to the ability to use either type of brace with macros?


Yeah, you can use any type of delimiter with macros, but I think that's an important feature, especially since macros can expand to declarations. Early versions of Rust required parentheses for all macro invocations, and that looked really ugly for anything that looked like a block. Delimiter interchangeability is a feature from Racket that works really well when it comes to macros and I'd hate to give it up.


Agreed. They allow you to naturally create vectors with the bracket delimiter `vec![1, 2, 3]`, create spawn-like macros with the brace delimiter `spawn { code }`, and in the normal case just use parentheses `println!("testing")`


> () is used for function calls, grouping, and tuples, while [] is used for array literals and array indexing.

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.


> 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.


This is an orthogonal issue. Arrays are clearly partial functions. The Try[.]-vs-exceptions issue is to do with the question whether failures should be reflected in types (the Try[.]-option) or not (exceptions). There are good arguments for and against both options, but both options go well with the understanding that arrays are functions and indexing is function application.


I don't think anything's much orthogonal to partiality of functions in languages with strong type systems, unfortunately :)

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'm not sure I can agree. I didn't say that the type of the array function is independent of the choice of error propagation mechanism. If you think an array is a partial function from integers to some value domain, say A, then in the exceptional POV the array type is int -> A, and in the Try[.] POV the array type is int -> Try [ A ].

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.


OK I can agree with that, I think I was taking what you wrote as a wider endorsement of Scala's approach than what you intended.


What about '{}'? It's a nice pair of opening/closing characters.


[flagged]


It's so sad, because I yet to see a single place where they're addressed.


It's not sad, because the examples are never brought up.

It's akin to saying Go is horrible because of all the templates and leaving conversation.


Yes, but getting 8 downvotes in two comments ... why should I care?


Who cares about downvotes? It's just arbitrary points.


It says a lot about Rust's culture. If they are not only _not_ interested, but actively hostile towards input, why should I bother?

Just look at the replies. Sure, it's possible to hand-wave issues away by setting the standards low enough with "Java did that mistake, too", "we just copied that from Python", "this looked more familiar" ... I think no one disagreed with that.

It's 2015, and having marketing along the lines of "we only made half of C++' mistakes" just doesn't cut it for me anymore.

For other people it might be good enough, but I'm worried that Rust won't get enough traction if the language is not drastically better on average than its incumbents, but just messy in different ways.


First off, Rust != Hacker News. You might have been downvoted by someone unaffiliated by Rust on HN. For example I'm affiliated with Rust community but didn't down-vote you.

Second, let's review your post:

You never supplied examples to your comment of using some abbreviation only half of the time.

Your point about Rust using [], () and {} for functions is wrong, because it only ever uses () for function calls, others can appear in macro.

Your example of using <> vs [] is a subjective choice, that doesn't have any scientific or even majority consensus. To make matters worse, one of Rust's side goal is to somewhat ease migration of C++ programmers to Rust. I actually agree with you on this point, but I realize most people would find this syntax confusing due to habit of writing array literals using brackets.

On the other hand underscore python style that is proven by a study[1] to be a better choice for readability you consider the less usable one.

[1] http://www.researchgate.net/profile/Bonita_Sharif/publicatio...

Could you improve your post with examples and scientific studies demonstrating your points and post it to http://internals.rust-lang.org/ or http://users.rust-lang.org/ ?


It is perfectly fine that many design choices in Rust evolved from "let's copy what $random_language did", but mentioning the drawbacks of that kind of approach to language design suddenly requires "scientific studies demonstrating your points"?

Are you kidding me? That's exactly what I meant with "Rust people are actively hostile toward input".

It is even more "I don't want to hear what you say" than it appears on the first look, because we both know that counting all "scientific studies" about language design probably wouldn't require more fingers than a human usually has.

So all we have is some empirical knowledge. You even got some findings for free! Things no one ever said:

  - "Having two different ways to call/invoke/execute stuff
     made it much simpler to learn the language."
  - "Making the casing of types inconsistent really helps 
     when reading code."
  - "Dropping random characters in functions and types made 
     me feel at home coming from C! I would be terribly 
     confused if ptr was called pointer, vec vector, fmt 
     format, iter iterator or rt runtime. The only thing I 
     don't like is FromStr, it should really by FrmStr 
     instead! .. I love str's rsplitn btw!"
  - "Calling Strings Strings and String views str ... that 
     makes a lot of sense!"
  - "Having used both <> and [] extensively, <> is much 
     more readable."
  - "It was a really great idea to have special syntax for 
     arrays, so the language designers could overload [] 
     with two completely different meanings."
  - "Having both () and [] available is great because you 
     can write code where foo(x) and foo[x] do different 
     things!"¹
  - "Not having varargs really worked out! Nevermind that 
     most popular macros exist solely to emulate varargs!"
  - "Despite most macros not needing the do-whatever-
     you-want syntax, let's require ! for all of them. This 
     way, macro writers don't have any reason left to try 
     making their macros behave predictably! Isn't it great 
     that they can just claim 'you knew that all bets where 
     off when you wrote that !, so stop complaining!' 
     instead of fixing their macro?"
  - "Fucking up Generics so badly that it requires 
     different ways of writing them depending on the 
     context really paid off. It's certainly not an issue 
     which is hit by every person writing something Generic 
     for the first time."
  - "Inventing unnecessary differences between modules and 
     objects is great, because it forces you to think 
     whether you need to use . or :: and breaks use-sites 
     of that code when migrating from one to the other."
  - "Given all the lessons of terribly broken dependency 
     management systems out there – let's just ignore them 
     all and put every package in a global namespace ... 
     what could ever go wrong?"
The language could have been much better if Rust devs wouldn't have been so insecure and defensive, but now it's too late anyway.

So why bother evoking any more excuses in different places? Rust-the-bad-parts is basically unchangeable now.

So what's left? I think it's only honest if Rust devs would be more open about the mistakes they made, and wouldn't require outside expertise to point out the issues.

¹ WAIT ... that's actually what Rust devs said when they defended having both () and [].


    but mentioning the drawbacks of that kind of approach
    to language design suddenly requires "scientific  
    studies demonstrating your points"?

    Are you kidding me? That's exactly what I meant with 
    "Rust people are actively hostile toward input".
For what is worth, I only meant it in case of underscore, since underscore syntax has scientific proof it's more readable, if you are aware of a better or another study, please list them. Also I asked for examples.

But here let me look at your complaints:

1) Not sure what you mean in this case, could you elaborate

2) Type casing is inconsitent. All types are CapitalCamelCase. Only special built-in literals are lower case.

3) Fair point, a bit subjective, but fair. I like the terse syntax tbh.

4) Well, String represents mutable and str represents immutable strings, which also double as string views.

5) Fair point, I prefer [] for generics, but again it still falls under subjective.

6/7) I'm unsure what you mean. [] is used for indexing arrays or slicing a piece of it. I think Python had something similar.

If you have foo(x) that's always AFAIK calling function. foo[x] means it's calling index or slice operator on a foo. In either case it's taking a portion of foo.

7) see 6

8) That's actually a fairly good point. I think some kind of optional parameters or varargs were planned, but unsure of current situation

9) Not getting what you mean here? Whenever you add extensibility to language via macros or custom operators, you get weird language DSLs that are incomprehensible. At least putting macro_name! tells you next part is DSL and what's its name is, so you can look it up.

10) Fucking up generics? What?

11) Not sure what your point is? Rust doesn't have objects. It has structs and it has traits. Modules are merely way to organize code. I think you are probably referring to OCaml like modules/objects, but unsure what.

12) Fair point. I'm unsure what alternatives you have in mind? Using URLs?


  1) You basically have both () and [], wasting one of the scarcest
     resources in syntax (brackets).

     Some people might argue that getting a value from an array is totally 
     different from getting the same value from a function or a map, and
     deserves special syntax. If we look at languages which did away with 
     this made-up difference we see that pretty much no person is missing 
     it.

     Even if you disagree with this reasoning and consider arrays to be 
     something very special, deserving its own syntax – guess what? Rust 
     developers introduced the traits Index and IndexMut, so all the great 
     ideas about some type being special and knowing what happens when you 
     read [] went straight out of the window.

     If we dig deeper and try to understand why they did something so 
     astoundingly mis-designed, we basically hit an interesting issue:

       They overloaded their [] operator to do two completely different 
       things, depending on whether there is an & in front.
       Thing A: return the value at the given index
       Thing B: return an address pointing into the indexed data

     This of course doesn't translate well into the () syntax, given that 
     Rust devs are proud that they don't support overloading.
     (I guess []'s overloading doesn't count for some reason ... and doing 
     the little dance with traits doesn't count either.)

     Taking a step back, if it would have been considered acceptable to 
     pick two different names for doing two different things, the whole
     issue wouldn't exist, e. g.:

       Thing A: values(1)
       Thing B: values.at(1)

  2) Yes, we agree. Having different sets of rules for a) language 
     designers and b) language users has shown consistent results:
     It never worked out.

  3) Yes. I have nothing against terse syntax, it should just be 
     consistent. (I favor not abbreviating things though, because it's
     difficult to be both terse and consistent, considering that things 
     can be abbreviated in more than one way.)

  4) Yes, I know what they do. I just thought it was a nice example of two
     related constructs having different casing style and abbreviation 
     style.

  6/7) See 1). The sad thing is that [] can do whatever the developer 
     wants. All guarantees went out of the window with Index/IndexMut.

  9) What I meant was that not every macro wants to define some weird 
     language DSL. There are plenty of things which only exist as macros
     because the language lacks varargs. (vec! for instance.)
     If the language ever adds varargs, vec! can't just stop being a macro, 
     because that would break source compatibility: They would need to keep 
     the macro version of vec and introduce a new non-macro version of vec,
     and hope people migrate from vec! to the "new" vec.

     It's a design which makes the common 90% suffer for the benefit of the 
     most complex 10%. Sometimes it is worth it, but in this case I think it is
     not.

 10) I had the combination of picking a symbol which is both hard to read and
     hard to parse in mind.

     Every language that chose <> has weird cruft and therefore it shouldn't 
     haven been too surprising that Rust is also suffering issues, but in 
     Rust's case the problems have been shokingly severe. There are loooong 
     discussions about it if you want to read them. (So it's not just me.)

 11) Sorry, I meant structs.

 12) URLs are an option. There are also alternatives. I think the essence is
     that it's possible to deal with these issues without entering Java's
     tld.mycompany.mycontinent.mygroup.myproject.mydogsmiddlename madness.
I hope this cleared some things up. :-)


Re 1, 6 & 7, as we've discussed before: languages that do away with the () vs. [] distinction don't have the same restrictions as Rust, e.g. Scala doesn't have explicit addresses for objects, meaning `&x[i]` in Rust (get the address of the `i`th element) has no first-class equivalent in it. It isn't possible to implement this by just overloading `()` without introducing dramatic new language features.

On this note,

  They overloaded their [] operator to do two completely different things, 
  depending on whether there is an & in front.
is entirely wrong: `x[i]` is literally equivalent to `x.index(i)` or `x.index_mut(i)` depending on mutability, so the `&x[i]` form is the same as `&x.index(i)`, and `&` is a no-op. There's no special magic other than a slightly subtle desugaring.

No Rust dev is proud that there's no overloading... that sentence doesn't even make sense, as every operator can be overloaded, including the () call syntax.

Now I agree that there's often room for improvement in designs, but hammering on a relatively minor syntactic point (especially one that follows many, many other languages) for months seemingly without understanding the current design and the reasons for it is quite tiresome.


1) Sure, pair symbols are a bit rare. But they aren't as rare as you set them to be. Symbols like / | or \ can act as substitute for bracket like syntax.

    > If we look at languages which did away with 
      this made-up difference we see that pretty much no  
      person is missing  it.
Eh, not sure what languages are these and how you think they relate to Rust. Keep in mind Rust goal almost from the start was something that is familiar to a people that worked with Gecko engine. With that in mind, I can't think of a similar family language that did this.

You know what happens when you call Index/IndexMut. To say it isn't so, is outright fallacious.

Also Rust takes the probably best road of allowing limited operator overload, which IMO so far proved better than allowing no operator overload or allowing custom operator overload - which is subject to weird rules and/or leads to heavily overloaded code.

Also [] always does same thing. For given Index value it returns value found at that index. & is not an address. AFAIK address are never allowed unless you drop into unsafe. It's a borrow reference to Output value. Index returns immutable version, while IndexMut takes mutable version and returns mutable output value.

2) No we don't. I think having user and built-in defined types is perfectly ok. In any non-Lispy langugage there are special literals, which can't be handled by user defined ways, unless you allow for some funky grammar.

4) Related yes, same no. In same way i32 and u32 are related but not the same. As I said in previous question having some subset of language be unable to modify is IMHO a good thing.

6/7) They can't. You can tell by mutability of array which one will be called and which one will be returned. You CAN'T EVER change type of mutability of result.

9) Whenever I see macros/operator overload/syntax being given too much freedom, it always ends up with incomprehensible DSL-s that you can't disentangle from regular code.

10) Subjective opinion. And it could be trivially solved with better type aliases.

12) I don't think it's a big problem and a fix to Cargo that adds a special unique identifier should be trivial to implement, although not necessarily forward/backward compatible.


What multiple Rust proponents? Where? What people consider [] superior to <>? How many?

Your text have an air of superiority but your arguments leave a lot to be desired or to be more frank [Citation needed].


    > Nobody claims that <> isn't "familiar". People are claiming
    > that <> isn't very good, and they are correct with that.
    > Those languages which made <> "familiar" picked <> because
    > they added templates/generics/... after the fact and tried
    > to bolt them onto an existing language without breaking all
    > existing code.   
What people are claiming this? Is there some argument

     <Type, <Type<Type>>> 
is much better than

     [Type, [Type[Type]]]
in monospaced fonts? I agree I prefer it, but I know it comes down to subjectivity and familiarity. Not the most compelling reasons.

  >      impl Index<usize> for MyFunnyArray {
               type Output = u32;
               fn index<'a>(&self, _index: usize) -> &u32 {
                   &self.arr[random::<usize>() % 3]
               }
           }

           impl IndexMut<usize> for MyFunnyArray {
               fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u32 {
                   self.arr[random::<usize>() % 3] = random();
                   &mut self.arr[random::<usize>() % 3]
               }
           }
Sure, you can write that, but you can also write:

      fn getIndex(&self, index: usize) {
           self.arr[random::<usize>()%self.arr.length]
      }
Rust won't protect you from stupid mistakes. The only question is whether it will allow you to override index syntax.

      myArray[3]
      myArray.getIndex(3) 
I actually prefer this small token of power, as opposed to having to write:

     date1.compareTo(date2) < 0
     date1.equalsTo(date2)
     date1.index(at)
That gets tedious fast.

     > As you might see, they are called i32 and u32, not i32 and UnsignedInt4Bytes.
Correct, because they are both built in literals. String isn't a built in literal. You can't have:

      u32::new()



     >Not sure what you suddenly try so say with "you can't
     >modify this", "you can't mutate that".

     >As shown in 1) [] can do whatever the developer implements.
     >Sure, the usual language rules apply, I think nobody 
     >disagreed on that.
No matter how many time you call Index on your array it stays the same. What kind of trippy Index you want to implement is still there, only with uglier syntax.

      > Yes, that's exactly the concern I'm raising here, because that's what Rust is encouraging.
It's not. All macro invocations are fenced in by macro calls. All syntax extensions are followed by extension calls. Could macros/syntax extensions do horrible things? Sure. So can regular code.


Not only are all your points about Rust incorrect, but numerous people in both this thread and past threads have repeatedly told you why they your points are incorrect and yet you persist in believing that they have been unaddressed. At this point it's safe to say that observers have caught on to your campaign of indefatigable misinformation and find it easier to downvote your comments than to waste any more of their time making arguments that you will proceed to blithely ignore.

In fact, it says a lot about Rust's culture that people were willing to engage you constructively in the first place despite your acerbic and technically-naive ramblings.


[flagged]


Wow. That response was... Childish.

I might agree Rust might need improvement in some areas but I don't agree with your sentiment or tone.

Not to mention you are quite puzzlingly wrong on many accounts.


> Not to mention you are quite puzzlingly wrong on many accounts.

I'd love to see an example where this is the case.


The most egregious is [] is used for Thing A/ Thing B.

Another is Python underscore syntax being less readable.


In this example the dichotomy is between String (which is guaranteed by the type system to be valid UTF-8) and OsStr (which might be in an unknown encoding or otherwise not decodable to valid Unicode).

This is exactly when you want a systems language to require explicit conversions, rather than converting things silently and possibly losing or corrupting data.


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.


The problem there is overblown - it's basically all due to the idea that sys.stdin or sys.stdout might get replaced with streams that don't have a binary buffer. The simple solution is just not to do that (and it's pretty easy; instead of replacing with a StringIO, replace it with a wrapped binary buffer). Then the code is quite simple

    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.


There is no ~str, but when there was it was largely identical to what String is now.


Good job, guys! This is an exciting language. Really happy about the "native" language revival going on lately.

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?


They should absolutely not say to use `old_io`, that must have slipped through. New IO has fully landed, you can't even use `old_io` in the beta. Which page? I'll fix it right now. :)


http://doc.rust-lang.org/nightly/std/index.html

"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"


Ah, thank you! We used to mention it from io's page, and I removed that, but this one slipped. https://github.com/rust-lang/rust/pull/24022


Looks like libstd/lib.rs still points to old_io, which shows up on http://doc.rust-lang.org/1.0.0-beta/std/

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


I just submitted a fix for the first, the second I'm not as sure about, and I just opened up https://github.com/rust-lang/rust/issues/24023 for the third. It's non-trivial because Duration isn't yet stable, I thought it might be, so I didn't prioritize updating, but it didn't quite make it in. Thank you!


Shouldn't the third be trivial, since thread::sleep_ms takes a u32?

Still building beta, but shouldn't this work? https://github.com/rust-lang/rust/pull/24024


Ahhh, so this was one of those last-hour changes, specifically so this works out, iirc. sleep will take a Duration, sleep_ms takes a u32. Anyway, let's keep talking in-ticket.


The doc's, and more specifically the tutorials, can be a bit stale. If you're referencing the one I think you are (building the number guessing game) it's old. I'm sure it will be updated eventually. [1]

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.


Question for Rust peeps out there:

Is there a documentation on how to build single binary executable for Rust?


I'm assuming you're referring to static linking.

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?


Name resolution libraries are loaded dynamically [1,2], so glibc can't be linked statically if you want NSS to work. I gather there might be a way to explicitly link to a specific resolution library but this is practically unsupported/poorly documented.

[1]: https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked...

[2]: http://stackoverflow.com/questions/3430400/linux-static-link...


I understand this limitation. I can currently get around this by using --enable-static-nss.

Does rust currently expose any way to do this? Could I perform linking myself as a workaround?


> 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.


Wonderful -- thanks Steve! Best of luck on the upcoming release.


It's generally considered a poor idea to statically link glibc, but if you don't rely on certain features, you can do it. For example, NSS won't work unless you add a bunch of flags for other libraries you specifically use, see https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked...

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


I'm looking forward to using rust in a constrained embedded environment where I control 100% of the software. I don't have to worry about such problems.


Awesome, then yes, you have several advantages here :) You'll be using nightly Rust for a while, as several features that target this use case are still unstable, like inline assembly. But it's a use case we do care a lot about.


Most of the people I've seen targeting this use case stub out their own stdlib, although that obviously gets cumbersome the more stuff you want to do.


If you statically link libc, and then another library you use (potentially transitively) dlopen(3)s a library, you are going to have a bad time.


Of course, but that doesn't mean we shouldn't have the option, right?


Is there plans to drop glibc dependency? Rust should be able to do system calls itself and glibc should be as optional FFI library IMO.


That's funny, I always thought that GHC's lack of support for shared libraries and requiring static linking were serious obstacles to its adoption, but people are actually asking for this kind of thing now?

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 :)


May be I misunderstood something, but dropping dependency on glibc doesn't imply static linking.

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.


Does Rust have a UNIX dependency? If not, I don't see what the UNIX philosophy has to do with anything.


Possibly glibc itself, but not a libc in general. While it's true that Rust can make system calls itself (though inline assembly isn't stable yet), we're not interested in re-implementing all of that.


Finally. I had been holding off on implementing things in rust because of the time spent getting libraries up to date with the nightlies. This is huge for the rust ecosystem


Indeed. So many times over the last few months things just suddenly broke. I understand and all but it will be nice to see some stability.

I really feel the community, which is already awesome, will start to build new things at a rapid pace.


I am somewhat underwhelmed by this because I can't really come up with a problem that can't be solved by C++, Java, Python or Go (or some combination hereof) which could be solved by another low-level language without garbage collection.


❯❯❯ brew install rust Warning: rust-1.0.0-alpha.2 already installed

:(


Sub in the version and sha256 and you should be good to go: https://github.com/Homebrew/homebrew/pull/38346/files


Actually, this isn't enough. You also want to set --release-channel=beta, otherwise you effectively get a nightly build of the beta tarball (unstable features available) instead of a proper beta channel build of that tarball.





Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: