So yeah, 1.26 is the most substantial release since 1.0, but there's lots more goodies coming in the pipeline. :) It just so happened that all the initiatives from last year are preparing to land at approximately the same time. For example, coming up next in 1.27 is stable SIMD: https://github.com/rust-lang/rust/pull/49664 (though only for x86/x86_64 at first; more platforms and high-level crossplatform APIs are on the way).
The features I'm highly expecting are pinned references and async/await, that will make most of my paid Rust to be so much more readable and maintainable. I know these are coming this year, but any chance they might be already in 1.27?
1.26 has impl Trait, which is one of those things that really makes your life easier as a Rust developer. I've been using beta already with the new API I'm building just to get that feature, now on stable right before I'm actually thinking of deploying the API. Nice.
The RFC for async/await was actually just accepted two days ago! https://github.com/rust-lang/rfcs/pull/2394#issuecomment-387... :) It's a priority for this year, but it'll be a few months yet; unless something goes unexpectedly wrong, I'd expect it no later than 1.30, releasing on October 25th.
It's been three years since 1.0, or at least, it will be in five days :) I'd say one mega-release every three years is pretty... slow, honestly.
That being said, we'll be adding new language features as they fix pain points for users and fit in with the rest of the language; when that's not true, we won't move so quickly. Rust is still fairly young, there's a lot to do!
I recently found myself inserting &/*/ref/ref mut into match expressions before I'd even seen a compiler error. My first thought was "aha, look at how experienced I am with Rust now!" Followed immediately by "I can't wait until this isn't something you have to learn to be productive with the language." And now that day has come! Really exciting for me.
I also need to go and find all of my impl Trait TODO comments and get to work on cleaning those up! What a good day.
I should also say that while I haven't read the second edition of the book yet I am excited to have some new Rust-related content to read. The first edition of the book was a really eye-opening learning experience for me (both about computers and Rust and also about how to build a community and prioritize teaching) and I can only imagine what an improvement on that is like.
Nicer `match` bindings was easily my favourite feature of this announcement. I can't recall the number of times I've been trying to show some rust code to someone and then pattern matching comes in and I get a cold drip of sweat expecting their reaction/questions as to why do we need to be so verbose. Happy day for me as well indeed!
The guarantee of `impl Trait` is that if I want to call a trait method on the returned object, e.g. for iterators if I want to call `foo(0).next()`, then the function pointer will always be in the same place on the object in memory (static dispatch).
By contrast, if I returned a boxed trait, then calling the trait method requires a dynamic lookup to find the method on the boxed object, and then jumping to that function (dynamic dispatch). In this example, `iter.map(..)` and `iter.filter(..)` return two different implementations of the Iterator trait, so dynamic dispatch is required.
In general, if your function returns multiple possible implementations of a given trait, then the compiler cannot know where the trait methods will be statically, so it is impossible to do static dispatch. Since impl Trait wants to guarantee static dispatch, it requires that only one possible implementation of the trait be returned.
I think your explanation of static and dynamic dispatch is either wrong, or incredibly confusing.
The offset of the function pointers is always statically known for a given trait/interface type, it's just the actual vtable instance that may not be known, ie. the concrete type implementing that trait/interface. A statically known vtable instance that can be inlined/monomorphized is static dispatch, and if it's not known at compile-time it must be dynamically dispatched.
That's not the clarification I was making. For instance, I simply can't interpret this line from the original post as anything but incorrect:
> then the function pointer will always be in the same place on the object in memory (static dispatch)
As I said, static vs. dynamic dispatch is about knowledge of the vtable instance, not about the offset into the object or the vtable. All of the offsets are always known statically, it's merely what you're indexing into that may or may not be known statically.
Maybe I'm being pedantic, but I've found that there's a lot of misunderstanding surrounding static vs. dynamic dispatch.
I see. Makes sense. It might help to show what the representation in memory of a trait object is. (I'm assuming there's a type for it somewhere in rustc, but I don't know where offhand.)
The mismatch isn’t about the associated type, it’s about the actual, underlying type. One is a Map and one is a Filter. It’s not possible to determine which is returned, so you inherently need dynamic dispatch.
There is some discussion about it possibly being sugar for an anonymous enum in the future, but that’s not what it is right now.
I'll take a stab at explaining this in the way that I finally started grokking the issue (coming from the land of Java).
In Rust (most languages), by default the compiler needs to set aside space on the stack for each return value. So it needs a constantly known size (and shape) to create the slot on the stack. You need to flip to dynamic dispatch, ie a pointer to an object (Java's default), that will be placed on the stack as a reference to the unknown size/shape of the thing at the end of the pointer when the size/shape is unknown. A pointer always has a constant size on the stack.
In this example, `impl Trait` is just saying I want the compiler to figure out the size/shape of the thing being returned for me, and allocate that to the stack at the call site of the function. What this means is that even with `impl Trait` you must return a thing that has the same size/shape.
Steve's answer mentions a common pattern used to create constant size/shape by using an enum for the wrapper type to return two different types on the stack from the function. The only other option is to put something with unknown size behind a pointer, ie Box<Trait> or &Trait, and thus pay the expense of dynamic dispatch.
My understanding is that impl Iterator<Item = i32> in return position means that there is some single concrete type that implements the Iterator trait that we are simply not going to name. This way, the compiler can do dispatching statically. If different paths returned values with different types there wouldn't be just a single type that is known at compilation time. That is why the extra indirection via trait objects is required in the example.
I think because returning two different types requires dynamic dispatch when using the returned objects, which requires Box; if the function only returns one type then that type can be determined at runtime and further function calls can be implemented with static dispatch instead.
The proxy object would have statically known size (maximum of the size of the types it dispatches between, plus some metadata such as a vtable pointer or an enum discriminant). Now, because you know the size statically, you can store it in the stack.
Tangentially, do you know why Rust never included an Either type? We have Result, which is great, but Either is useful for more things than errors (though that seems to be it's main use).
I feet Rust was getting pretty boring... and that's amazing! As a hobbyist I'm pretty happy to see the language maturing. I remember release 1.0 and thinking it was maybe too rushed. How wrong I was!
Is there anywhere I can get a quick recap of major features Rust is planning to eventually maybe implement, and their state? I mean major features like NLL, impl trait, etc.
IIRC: NLL are nightly already, const generics in planning stage, and what happened to CTFE? For us not in the know, a sneak peek on what's coming/being discussed is very exciting, but browsing the issue/RFC tracker is sometimes a PITA and it's hard to keep up to date, especially after getting used to the workarounds.
Yes and no! We’re gearing up for a big release, and so a lot of stuff is landing. I don’t have that text ready at the moment but rest assured that in the next couple of months you’ll see something. “2018 Edition” :)
Amazing work on this release! I've needed so many of these features in stable Rust, and I'm so excited to get to use these.
I mean, just yesterday I had to do some weird stuff to a for loop in order to iterate over all u8s in a cleanish way.
Lovely, lovely work. I'm especially excited to see how clever the ref/ref-mut `match` inferring is. If it's reliable, that's gonna remove quite a bit of friction for newcomers. So cool!
With the release of impl Trait (easily the feature I've been most looking forward to), has there been any talk of/proposals to allow anonymous trait implementations? (see: https://github.com/rust-lang/rfcs/pull/2406#issuecomment-384... for an example of what it might look like)
Being able to return anonymous trait impls from inside functions could eliminate a lot of what seems like boilerplate structs/impls from Rust code. As an example, the code in futures-util that adds combinators to a Future defines a type (Then, Fuse, Map, etc) for each combinator function. With those functions now able to use 'impl Future<...>' as the return type, being able to actually type 'return impl Future<...>' could make code like that a lot less cluttered.
Congrats on the release, I've been following Rust for a long time.
While I'm sure it won't mess anyone up, the range for the i128 is hard to interpret. It looks like -xxxxx - xxxxxx but with the extreme number of digits it looks like two negative numbers or a subtraction problem.
This more than makes up for a couple of slightly sparse releases. Congratulations to the whole team, and to you especially for the completion of the new book:
Everyone keeps talking about impl Trait (which is great) but I'm super pumped for ? working in main now. Was recently writing some code as I finally got back to rust and forgot about that edge and had to write a match block when ? would have been good enough (felt silly to make a method just to handle that).
The wonderful thing about Rust is that despite being fairly new and rare to get paid for working on it, it is still enticing( to most programmers I've met). The consistent effort to improve it is really paying off. I hope it gets to a place where it's `batteries included` like Python. There are some glaring holes in the stdlib I would like to see fixed sometime soon.
The argument for putting things in third-party "blessed" crates rather than the standard library is that it allows them not to have to follow the versioning guarantees of Rust itself. Given that the core team has stated that they don't plan to have a Rust 2.0, this means that there wouldn't _ever_ be any API-breaking changes for things added to the standard library; by having things like `regex` and `rand` be external crates, versions bumps can happen independently happen, which means (among other things) that major version bumps are possible.
That's certainly an advantage. But for devs like me, we are used to prog langs providing a set of APIs in the stdlibs, that are officially blessed to be maintained. They might not be the best designed, but they are guaranteed to work for a long long time. This kind of trust is very hard to build for 3rd part libs. It's a trade-off to make, and rust for now has decided to keep stdlib lean and quick to iterate over. But I hope at some point, Rust devs would consider including more APIs.
I think that the rust-lang-nursery Github organization[1] is designed to solve part of the trust issue. A lot of the "batteries" crates are in there; `rand`, `futures`, `error-chain`, `lazy-static`, `glob`, `bitflags`, and `log` catch my eye after looking through. In the long run, I don't think that having things like the blessed HTTP library being third-party will be that much of a detriment; at least when I was writing Python, almost everyone I knew used the `requests` library, and nobody worried about it not being part of the standard library.
What sort of maintenance is actually needed though?
Rust promises that existing versions of crates will build and run in the future, assuming the crate is well-behaved and doesn't do anything illegal with unsafe code. That's part of Rust's general stability guarantee.
Maybe "blessed to be maintained" means crates get ongoing critical security fixes? Thanks to Rust, for most crates that's a very low-probability concern. (Obvious exceptions for features like HTTP and TLS that contain their own security decisions.)
Maybe you mean that all kinds of bugs will continue to be fixed and the quality of the crate will keep increasing? I don't think any languages really give you that; they all have standard library features that are effectively end-of-life. A lot of application developers don't even WANT such fixes since there's always a risk of breaking previously-working applications.
So I think it would make sense for the Rust community to commit to a subset of crates for ongoing maintenance, but for probably the majority of crates you could just keep using the exact same version for the next five years with no worries.
But the versioning guarantees are _exactly_ why having them in the stdlib would be good for me as a user; it means I'd be able to have code that keeps working, but have new improvements available.
It's totally fine to decide to make the trade off, but it's just frustrating to not have the downside acknowledged.
As long as you don't use "*" as the version for any of the crates in your project's Cargo.toml it should stay at one particular version and therefore follow SemVer guarantees. If you are REALLY paranoid, you can even have the crate be a particular git commit or you could download your preferred version and then refer to it using the "path" key in the Cargo.toml.
Adopting crates into the stdlib would make stdlib and the crate depend on each other, which is not a nice scenario. Also having certain "core"-functionality be dependent on a 6wk release cycle is not great for stuff that is not good once it's set in stone (aka released on stable).
Real core functionality, bare bones stuff like parsing of Rust itself, I/O and TCP/UDP connectivity, which provide an unlikely to ever change framework for crates to build upon rightly has its place in std; e.g. convenience methods for downloading (like what reqwest provides) OTOH have to stay flexible to adapt to the real world.
Do you know where I can find the comment about no plan for Rust 2.0? I tried googling it but I couldn't find it. Reason why I'd like to find it to have some guarantee that this is true.
Which glaring holes are there in your point of view? Rust devs are on HN a lot, so maybe they will see your comment.
All that said, Rust definitely errs on the side of preferring to put things in 3rd party crates instead of stdlib, even for things that are very common to put in std for other languages (e.g. random number generation).
These are really interesting examples. In Python, the HTTP client in the standard lib is pretty crappy compared to requests, which is the defacto standard. Likewise, the CSV library in the Python stdlib is not very good, but can't improve much for compatibility reasons.
Developers have different needs. Some value stability, some value bleeding edge features. Trying to put libraries in the standard library and freezing their interface is really tricky to balance.
Though the ecosystem is still young, the long term story for "batteries included" in Rust is going to be a rich ecosystem in Cargo, not a rich standard library.
An interesting compromise might be a crate maintained by the core team (std-ext?) that simply re-exports crates under generic names that the community has blessed as being, for better or worse, the default in the Rust ecosystem.
So someone who wants batteries included could add only std-ext as a dependency and get hyper as std_ext::http, regex as std_ext::regex, rust-csv as std_ext::csv and such to avoid needing awareness of the entire ecosystem when first starting out. Seasoned Rust developers would probably continue pulling in dependencies directly, but it might improve the experience for new Rust developers to have everything a 'use' statement away without needing to go look for it.
What if a 'winner' today is a loser in a year? What if we come up with a new approach to solving a problem that requires a breaking change, or a new crate?
Would someone be inclined to try to improve the state of HTTP given a good enough version in std? Would we be ok with discouraging that sort of competitive approach?
To be honest, all I see are downsides to having a huge std lib. The benefit seems to be that there isn't an obvious, de facto choice for what crates to use. But I think that's a problem better solved by crates.io.
I tend to agree, but I also think that there's a little bit of nuance here. For one thing, I have definitely enjoyed using a large standard library...when I didn't have access to cargo or tools of similar quality.
My impression is that many of those who are asking for Rust's stdlib to grow are also/actually asking "please make it really easy for me to use these APIs that I care about," to which I would respond "it's OK, code can be easy to reuse even if it's not in the stdlib," "try cargo," and "crates.io needs to continue improving on discoverability."
To make it into some industries it needs to also be easy to grab a snapshot of all the batteries and do mods and licensing checks/approvals before doing a network unconnected install. Running a crates clone may or may not make sense. For some cases grabby an image on a thumb drive or DVD and doing a standalone install on a single machine makes sense.
I think we're likely on the same page. The desire to make writing an HTTP server that much easier by providing a quick-to-use solution is entirely reasonable.
I just don't believe the solution is some 'sanctioned', permanent addition to std.
Perhaps a better idea might be to include the most popular client in some form of official tutorial / guide / documentation, or as part of some extended documentation separate to the language doc?
That way libraries can coexist, but new developers have a single doc location to go to to find really common usages.
Needing to use two separate third-party crates (lazy_static and maplit) to have a global constant-initialized hash table is the one that surprised me recently.
Sure, maplit's hashmap! is just a convenience macro, but then so is vec!. Hashtables are a pretty fundamental data type and they deserve to be easy to write constant initializers for, just like vectors.
'const fn' seems like a red herring -- I don't want to write const functions, because I don't want to write code to initialize data structures at all. I want to write representations of data structures...
Sure, I think it's a useful crate. But it's certainly not "Need[ed] ... to have a global constant-initialized hash table" that's all.
const fn isn't a red herring; you won't be writing const fns, you'll be using them to do the actual initialization. If maplit would use them internally, for example.
"Speaking of print, you can pre-order a dead tree version of the book from NoStarch Press. The contents are identical, but you get a nice physical book to put on a shelf, or a beautifully typeset PDF. Proceeds are going to charity."
Black Girls Code, in my opinion, is a great organization with a well executed mission and good reach (several major cities throughout the US). Glad Rust picked them.
Source: I've volunteered for them in the past as an instructor.
> Inclusive ranges are especially useful if you want to iterate over every possible value in a range
Out of curiosity, why was the (or an alternative) choice not to make the compiler understand that the 0..256 was not inclusive, and somehow correct the literal value to do what's intended? Would that have been unusually complicated or?
Edit: Overall, still an amazing release, this was just the bit I'm curious about :) Great work by the whole Rust team/community
Huh, given the context of loop ranges over integers, when I saw the EWD link in that thread, I guessed it would be the "Why numbering should start at zero" one. Instead, the one linked ("On the cruelty of really teaching computing science") is about 10x longer, and it was an interesting glimpse into his philosophy.
Definitely agree that having the inclusive syntax makes sense either way (..= was mildly jarring at first but makes complete sense syntactically) - I just mean the exclusive ranges arriving at that compiler error definitely seems unexpected
Ah! Yeah, I mean maybe. At the same time, special cases can make things harder to understand. This would also technically be a breaking change, though probably not changing any realistic programs.
There is actually a simple reason for that: the ranges might not be constant literals. So the end value must be an expression of some type; here u8. u8 can hold only values 0-255; 256 is out if its range.
Unfortunately I don't have a good full response to this (otherwise I could contribute it and actually help), but off-head I was surprised by the lack of a `cargo install` (https://github.com/rust-lang/cargo/issues/2179). A few minutes later though I was used to googling for the package i wanted, and getting the version I wanted to use and adding it to Cargo.toml, and that's arguably a better way to do things to start with.
I haven't used it enough to have much more to say -- I've found everything pretty ergonomic so far (especially for a C/C++-tier language), I was most frequently confused when thinking of the most idiomatic way of doing something, but that's remedied by reading more (the rust book first/second edition, the rust cookbook). This should change over the coming weeks.
This isn't a particularly useful comment, but I chose Rust over Common Lisp recently for this new project, and I've found the std library's google-ability to be excellent for rust -- very few pages about steel have come up so far ("result rust" in DDG brings up rustlang related links). When I explored CL, I did not find that to be the case, but maybe I just didn't know where to look -- hyperspec is close but it is a terrible document to navigate through, and the 90s graphics didn't help (though they were nostalgic). Rust documentation is very often concise, well structured, and passably beautiful. I was also pleased with the Abstract Data Type solution in rust -- tagged unions. Some code:
The abstraction enabled here is just right for me, super similar to code that I'd write in Haskell, and helps me abstract over errors thrown by utility libraries (in this case `toml-rs`).
You may want to see the failure crate. In my practice most errors cannot be handled by a program and the only sensible thing to do is to pass an error description to a user or a programmer. Such fine-grained error type information as in your example is rarely useful. The failure crate provides a way to create detailed error descriptions (see `Context`) and fixes shortcomings of the standard Error trait.
Concerning keyword args as I remember them from Python you would probably have to hack something together with an Into<Option<HashMap<some_type_for_arg_names, some_enum_with_variants_containing_acceptable_types>>>
Concerning "named paramters" I think you would have to apply some of the same trickery in order to be able when calling the fn to leave out args or give them in a different order than specified in the fn header.
Or what I've sometimes seen written was (e.g. for configuration passing) structs created with Option<some_type> that were then passed to one or more functions which in turn get the values (named params/default args if you will) from that "object", being able to pick and choose the appropriate values to get in a given function/method.
This and not being able to do paramterized array sizes for things like SoA, AoSoA where the two things that I feel like were missing from Rust. Really happy to see the first landing(and I understand work is going on for the second).
There was some sort of SoA derive going around, I thought. Regardless, you’re right; we’re hoping const generics lands in nightly this year and stabilizes early next year.
I wrote a program yesterday in 1.25 which did some simple file I/O right there in main, didn't need to be fancy at all... and then this morning I come online and find that we now have fs::read_to_string and main can return a Result... great!
But I needed this yesterday!
Seriously though, this is an amazing release and so much stuff in here I've been looking forward to for ages. impl Trait is going to change the way I write Rust.
let example: (String, String, String) = ("ref".to_string(), "to".to_string(), "ref".to_string());
let example: (&str, &str, &str) = match &example {
(ref1, to, ref2) => (ref1, to, ref2),
};
Which might look confusing to people, it's converting from &(1,2) to (&1,&2)... but it's still type safe. Besides something like this, is there another reason to be worried about it?
Reading (or even writing!) rust code, you can get increasingly far without knowing what level of indirection you're operating on. I don't enjoy that. I don't personally feel like the notational burden for explicitness is enormous.
For field access/method calls, autoderef is a bigger convenience because we don't have the C++ -> operator, but I think I'd have preferred a syntax change here over the current behavior.
The match changes here don't bother me anywhere near as much as deref coercions do, because they preserve the level of indirection.
Deref coercion makes a &Box<T> behave like a &T (removing a level of indirection). Default binding modes only make a &(T, U) behave like a (&T, &U).
I've idly wondered whether it would have been possible to replace deref coercions with something like this. Making Box<T> behave like &T kinda works but loses you the ability to control `&T` vs `&mut T`, but maybe `x.y` where `x: &T` could "pass the reference on" giving you a `&U`.
Turns out the compiler rejects that with a "refutable pattern" error, because the args part of the filter/map closures do not handle the situation where the slice could be empty, which AFAIK can not occur when using windows/chunks/...
Maybe there has to be some special case handling for this pattern to be valid?
I'm super excited about the "Basic slice patterns". I've been learning some Elixir at the same time and was blown away by the different style of programming you can write in using matching. Glad to be able to try it out in Rust as well.
Existential types are great! Although I must mention that existential types are basically the way to model OO-style information hiding. It's a great tool to have in any mature type system, although I do wonder how it is compiled. Trait objects are kind of easy to imagine: a heap-allocated dictionary of methods for that trait. I do wonder how heap allocation is being avoided in this case.
EDIT: I didn't read closely enough. Only a single type is allowed, so the traditional existential type construct ("trait objects") are still needed.
There's intentionally no heap allocation or virtual dispatch when using `impl Trait`; if we were content with that, we wouldn't have bothered implementing it and just continued on as we were with `Box<Trait>`. :P
Oh wow, this sounds great! I've hit both the long signatures when returning iterators, and also the dereferencing song-and-dance with match, and my Rust projects have been very limited. So I think these are huge improvements that will help a lot of other newbies like me. I can't wait to start using 1.26 instead. Thanks Rust team! :-)
Yes and no. Think of it like release trains; the second edition has left the station, and so isn't being updated directly. It's actually pinned to 1.21; it left the station a while back.
Work on the "2018 edition", which is the next version after "second edition", is just starting. It will be getting updates as new stuff lands, though there may be a bit of lag. In general, docs are going to be a bit weird up to the Rust 2018 release; it's all coming together, but slower at first, faster at the end.
(This means that, as of right this moment, there aren't great docs for impl Trait. I'm working on them right now.)
This is hard to answer as a one-liner; do you know about monomorphic vs. polymorphic code and static/dynamic dispatching? Rust is, by default statically dispatched, so because the two kinds of iterators do different things, they have to be of different types.
Aside from the head start, I believe Go legitimately hits a sweet spot for developers wanting a language that's relatively easy to use yet also fast and fully compiled.
Rust on the other hand is targeted more towards developers that want C-like low-level control along with safety against shooting themselves in the foot.
The former audience is probably larger than the latter. But popularity isn't everything (JavaScript wins that contest ;)). The important thing is Rust is doing an amazing job in an area that hasn't seen much love.
Personally... I used to write Java and Python code (and enjoy it), but fairly straight forward code would just bog down on me. On the other hand, C and C++ code would always crash in frustrating ways.
Rust hits the sweet spot of not being a pain in my ass all the time.
> I know a bit of C, but not enough to feel like I can effectively use it
I love what the Foreward [1] in the Rust Book has to say about this:
"Traditionally, this realm of programming is seen as arcane, accessible only to a select few who have devoted the necessary years learning to avoid its infamous pitfalls. And even those who practice it do so with caution, lest their code be open to exploits, crashes, or corruption.
Rust breaks down these barriers by eliminating the old pitfalls and providing a friendly, polished set of tools to help you along the way."
Well, every two languages are different, but at least for systems programming, Golang unfortunately started to replace Python, but given how vitally important security is in that domain, Rust makes a lot more sense than Golang, won't you agree?
I don't think "systems programming" is a coherent concept anymore, so I'd reject the premise of the question. Go has already moved away from that word, Rust will be as well.
That you see them as being so different languages reinforces my premise above; they're good at different things, so comparing them doesn't always make a ton of sense.
Once you start putting "systems programming" and "Python" in the same sentence you can be pretty sure that the term "systems programming" has lost all meaning. Or at least using it without elaborating is only going to end up in confusion.
Perhaps one could say that about systems programming languages as well, including Rust. It'll be slow(er) to develop, and there will be much wailing and gnashing of teeth.
The word "systems" is increasingly relegated to a smaller fraction of the codespace than used to be; one wouldn't have considered building a high frequency trading system or a call handling system in a non C/C++ language in the mid 90's, but Java and Erlang get the job done well enough.
As always Alan Perlis said it best (in his "epigrams of programming"):
A programming language is low level when its programs require attention to the irrelevant!
gVisor is a security product (
Container Runtime Sandbox ) made by Google in Go and runs in production so I'm not sure what you mean by "how vitally important security is in that domain".
a) In terms of memory safety, Go is not memory safe in the presence of data races, and Go does not prevent data races at compile time.
On top of this Go does not enable ASLR on most OS's. That means that when Go loads code from other languages, such as C/C++, memory safety issues in those languages are extremely easy to exploit relative to in a language like Rust, which enables tons of mitigation techniques by default.
b) More loosely, Rust has a more expressive type system. It is, in my experience, much simpler to enforce constraints in a 'type driven' way. In my opinion, this leads to safer software.
As an example of (b) I have written authentication code that encodes the authentication protocol's state machine into the Rust type system. What this means is that it is impossible to jump between two states in an undefined way. Beyond that, because of Rust's affine type system, it is impossible to reference invalidated states.
For authentication code, especially when you add complex stateful transitions such as rate limiting, whitelisting, etc, this is an extremely effective way to reason about your security critical code.
I think there is a case to be made that there is a real, significant difference between the languages regarding security.
One could make an argument about data races, for example. Rust prevents them at compile time, Go does not.
You could also make an argument about compile-time guarantees more generally.
I'm not sure it's really a great argument, but you could make it. I think the parent's statement that it's only Go because it's made by Google is demonstrably false.
One could make the same kind of argument with GC lang vs none GC ( RAII doesn't protect from everything ). OP said "much safer" Rust is not much safer than Go, maybe it is safer but definitely not "much safer"
You're the one that said "Google is emotionally attached to Golang, which was born at Google." which is really stupid tbh you think engineers at Google pick up tech because they're "emotionally attached"? Those smart people took the tool that fits their needs.
I'm sorry, but Google isn't some role model with high values. On the contrary, it makes most of its money from selling our dirty underwear to merchants.
I'd say on the contrary. They chose it even while knowing they'll gonna need to hack generics. (Same in k8s, generics hacked together in a few different ways)
It's quite likely that, being at Google, they were already familiar with Go, people around them were using it, and there was a comfort factor with it being a Google project.
It's rational to take these factors into account, but they don't reflect on the merits of the language itself, and aren't necessarily transferable to other people choosing programming languages.
Also Go's 1.0 predated Rust by about 5 years. They started around the same time, but many people won't use a language that's not stable where their existing code may break with each new release. Go has had a ~5 year head start in developing a library ecosystem, tooling, commercial users, etc.
It takes a long time for a language to build momentum and a user base. Python was just starting to get popular when I graduated from college in 2005, and it was already 16 years old at the time.
This is true, but even today in 2018, Golang still suffers from the dependency-management hell, the notorious GOPATH, and many other issues it should not have being almost a decade old! Even Rust, which some call "lower-level language", which it is not, has Cargo after it learned from Ruby and Node how important is to have easy package management, and being able to install a new project without knowing almost anything about the language.
totally fair, but Go did stablize the language itself. i can with zero effort and 100% confidence run any old Go code i have lying around. that's pretty awesome.
I do a fair amount of Rust and Go, and I am not sure what axis you refer to with the phrase "killing". (Unless it is killing joy, for a lot of people </snark>)
Most apps out there are extremely serviceable with a language environment that runs up to 2x slower than what is possible with Rust. Youtube, edX, reddit, coursera all run on python, Facebook on PHP/Javascript, WhatsApp on Erlang.
In exchange for ceding low-level control, these languages provide a frictionless approach to evolving a system. You get dynamic features that are simply wonderful for creativity. Javascript is a blot on the landscape, but look at the effortlessness of d3.js.
GC is wonderful and does not gets in the way for most apps (from mobile to enterprise). Any time spent thinking about lifetimes sucks the fun out of coding. Lifetimes are fantastic for concurrent programming, no doubt, but there are simple patterns in Go that work well in practice.
Lightweight threads remove another point of friction; they allow you to model the concurrency inherent in a problem without much trouble. I'd prefer Go to get more of Erlang's failure handling and signal delivery mechanisms.
Go is a middling language, but its libraries and ecosystem are solid and have a coherent feel. They work out of the box without having to consult StackOverflow a zillion times.
The language I would love to have is Swift retrofitted with Erlang's process system.
Letting GC take care of lifetimes is a big plus for many applications. Rust's ownership model buys you a lot --- no GC overhead (space, not just time), fearless parallelism, more powerful static invariants --- but it costs you development effort, and depending on your application the costs can outweigh the benefits.
Rust improvements like NLL will hopefully reduce the costs. I think we will discover more ways to leverage the benefits, over time. However, I also think it might be interesting to let people opt into a Rust dialect that hides lifetime issues, e.g. using a lot of implicit Rcs and (Ref)Cells.
If the Rust ecosystem was up to snuff on the HTTP front, I'd be writing a lot more of it. At the moment, Go's HTTP implementation is just pretty much unbeatable. I'm confident it'll get there — and there's a ton of great work going on to get it there — but at the moment, it's still the Wild West.
Go also hits the sweet spot for CLI apps, in my opinion. The error handling is obtrusive and annoying otherwise, but for command line apps where you're just aborting whenever you hit any kind of snag, it's not a significant pain point.
I think this is a pretty major piece as well. Go was introduced many years earlier, backed by Google marketing and had strong HTTP services as a day one feature; it was designed for probably the most popular use case by perhaps the most influential fount of new tech. Given all that has Go really done all that well? Has it gone much beyond network services and a couple other niches (Docker/K8s etc?) It's not really displacing C/C++ in 'systems' use cases. It's not a go-to language for Machine Learning. The runtime+GC obviates most embedded work. You don't see any meaningful uptake of Go among Google's peers; Apple, Microsoft, Oracle, etc. I wonder just how important Go really is.
Frankly I don't know if Go passes any barometer for "important". I don't think it's significantly disrupting any particular space, so if that's the measure of a language's importance, I don't think Go's quite meeting that. I find it unlikely it ever will.
I don't think it was ever intended to be important in those terms, however. It was designed primarily to solve specific sets of problems at Google, but I don't think any sights were set on the C, Java or other related language worlds at large. From that perspective, I would argue that Go's actually been fairly successful.
I'd argue the Rust team is much more eager to be a real disrupter. I think in that sense, they're succeeding in ways and have not yet achieved success in others.
> Given all that has Go really done all that well?
My intuition is that the only languages that have grown faster are those that were blessed as "native" for a popular platform. For it's age it seems to be doing really really well.
But given Go's mix of advantages and disadvantages, should we expect it to be doing better? I doubt it.
Still to be seen if it becomes a Ruby or a Java, though.
Hyper 0.12 is right around the corner with the excellent h2 crate. We've been using the master branch on production the whole 2018 and it's super fast, stable and ergonomic.