Rust's borrowing system is pretty unique and allows for safe concurrency in a way that none of those languages can. Compared to Go in particular Rust brings a much more powerful type system as well.
Lower level, possible isolated unsafe code, more compilation checks, supported by Mozilla. More active and enthusiastic community than D, OCaml or Standard ML.
Over Go: higher level code, more powerful ergonomic abstractions (generics, macros, etc), and of course the lack of runtime/GC.
Over OCaml/Standard ML: more control over performance. It's also imperative first rather than functional first, which isn't neccessarily an advantage, but it is to people coming from a imperative programmig background.
The design goal of Rust is to have C++-like efficiency with all the benefits of modern programming language theory and practice, as well as some new innovations. In particular, Rust wants to address the two problems of templating/polymorphism/generics and mutability/sharing with zero efficiency or safety sacrifices. (I am not a Rust developer, that's just my impression.)
If you like OCaml/Haskell/etc but find yourself wishing you could make mutability and ownership more safe, or write very fast code more easily, and are willing to give up higher-order type system features for it, consider Rust.
Go is a very different language than Rust even though both superficially resemble C. If you're using Go because you want something C-like but better, consider Rust.
Go has very little in common with either of those languages except a GC. Go is statically typed, natively compiled, a good 10-100X faster, and it has great concurrency/parallelism stories. It also lacks classes and exceptions.
Funny, I feel the opposite, Go being more like C and Rust being more like C++; mostly due Go's strive for simplicity while Rust seems more comfortable embracing C++ level of complexity.
Go could be considered a modern C, in that it follow's C's spirit of simplicity and clarity at the expense of power and expressiveness, but it is often not used as a replacement of C for various reasons. One of Go's stated design goals was (and is) to achieve performance parity with C, but it falls short of reaching that goal in many cases.
With its type system and support for generics (in C++: templates), Rust has much more in common with C++ than it does with C.
> in that it follow's C's spirit of simplicity and clarity at the expense of power and expressiveness
Wow, I strongly feel the opposite about C. It is hardly simple or clear - understanding what your code will do after the compiler's done with it seems almost impossible, reasoning about runtime behavior similarly so. The complexity involved in managing memory, etc, is so significant.
But it is extremely powerful.
I see go as being extremely simple, but also very weak in terms for expressiveness, relying on the runtime for memory and concurrency management.
I was shooting for a simple answer, but perhaps some more clarification would be useful.
I describe C as "simple" in the sense that it's A) not a large language, by specification and B) somewhat by virtue of the former, there's generally a small range of possible implementations of any given problem (which helps C programs have "clarity"). Go is very C-like in that it also has both of these attributes, quite deliberately. C could be considered complex, however, by virtue of the amount of behavior that is unspecified/at the compiler's discretion. In that sense its safety often is difficult to reason about, but C programs themselves are generally easy to reason about. Go luckily doesn't have many opportunities for programs to be "complex" in that way.
C is "powerful" in the sense that it offers programmers deep control over memory. Given that, (effectively) anything that can be implemented in any other language can be implemented in C, with varying levels of difficulty. It isn't "powerful" from the language perspective itself — in terms of expressiveness or the features that it affords programmers — in the same way that C++ or Rust are "powerful".
A lot of the adjectives we use to describe languages aren't particularly well-defined, so some disagreement about them is almost a given. But hopefully that clarifies.
Rust's combination of no GC, unsafe escape hatch, and zero-cost C ABI FFI (both function calls and struct layouts) are among its biggest technically differentiating factors.
The FFI story seems to be overshadowed, but if you want to use things like Vulkan, DX12, etc... then C FFI performance becomes absolutely critical, and many languages are really bad at it.
I think the importance of selecting a C FFI is very much undersold.
You pretty much get interop with every major language for free. I've embedded Rust in Python, Java, C# and WASM/ASMJS pretty much with just that one feature alone.
Also makes the migration story really strong, which I think was largely driven by needing to be integrated into FireFox in a sane way.
I've gone further to say the freight train of an ecosystem behind and legacy effects of C mean any intended replacement should be fully compatible with its structure, calling conventions, etc. This lets anyone leverage existing C libraries or those in new language seemlessly. From there, the new stuff might all be done in better language with optional, incremental rewrites of older stuff.
Rust enforces proper acquisition and releasing of resources, and that is very cool. For that it uses linear types, which is beyond what ML's type system can achieve. It allows rust to manage memory safely with no GC. The type system can show the compiler when to release resources. I think that can be done in C++ and maybe Haskell but Rust has it baked in.
Think about all the low level resource management: network ports, files, heap memory... That's what rust does better.
Check ATS for a ML-inspired language with some resource semantics closer to rust and stronger control and correctness primitives. http://www.ats-lang.org
When I try to learn a new language, I typically want to: (a) read a lot of code people have written in that language and (b) implement algorithms that I understand well, while keeping in an idiomatic style.
When I tried to do this with Rust, I was shocked by how difficult it was to implement certain algorithms (specifically, operations on Linked Lists and Graphs). Maybe I'd do better if I chose a small project to work on, but it disturbs me to know that such elementary problems are so difficult to solve.
Without wishing to explicitly criticise your approach to learning Rust, I'll note that for the vast majority of programmers, it's likely to be frustrating for quite a while.
If you don't want to manage memory, why are you using any systems language? Python, JS, Go, and any number of other languages will be much kinder to you.
What I meant to say (hence the downvotes...) is that Rust's "borrow checker" is enough of a nuisance that this graph library replaced memory addresses, i.e. pointers, with indexes into an array, effectively creating its own virtual address space.
If you want to write a linked list like you do in C, just use pointers instead of references, mark your code as unsafe, and basically you have it.
I'm not suggesting this is a good idea, but I'm tired of the linked list argument which kind of implies Rust is much harder than C for an equivalent linked list implementation. If you explicitly choose to lose the extra guarantees, Rust is about as easy as C to write a linked list in.
I think I understand this. It's difficult to write concurrency-safe linked list operations in C too. This is a case where I would lean on the standard library for sure. Is there a different kind of problem you'd recommend to just dip your toes in?
Implementing a singly-linked list in Rust is much easier, because you can use `Option<Box<...>>`. (That is, each node in the list can hold the unique pointer to its tail, so it's easier for the compiler to prove to itself that everything you're doing is fine.)
Working through the Entirely Too Many Linked Lists book in general is super helpful, since it deliberately runs into the cases that are hard.
Implementing a hash table in Rust would probably be fine as a toe dip too. It's that multiple-references-to-each-object part that really burns you in the doubly-linked list case.
These are probably the two best sites for small, exercise training programs in Rust. Once you've gotten through a few of them, you'd probably be ready to build something useful, which might be more interesting.
Important correction: unsafe Rust is as hard as C or C++. It means that for many developers it's just impossible to write their own linked list in Rust.
Writing a linked list in C is an exercise for undergraduates. I hope that almost all developers can do it with a little effort. Writing a linked list in C is not hard. Using it correctly is the hard part. In a safe language like Rust you can write your linked list using unsafe code and then build a safe interface around it.
It isn't even a college level exercise to do linked lists in any language. The gp is probably getting at c being hard to write safe code at large scales, which I agree with, but linked lists are not large scale.
I really want to amend that. Writing a doubly linked list in safe Rust is very hard if you want to hand out references to its contents. If you're willing to use `Rc<RefCell<...>>` or `Arc<Mutex<...>>`, then it gets doable again (albeit pretty verbose), but you can no longer hand out pure references. That's because a reference means "I have statically verified that no one else can possibly mutate this things as long as this reference is valid." `RefCell` and `Mutex` replace some of that static guarantee with runtime bookkeeping, which is great for situations where your objects don't have unique owners, but it requires you to manage all your references with smart pointers, and your APIs can't hide that (in safe code).
Ok, so author needed to know C extremely well (i.e. "language lawyer"), comfortable with separation logic, able to turn informal requirements into specifications, use of ghost code, and have a SMT solver. The solvers often fail on some stuff that requires formal proof by rare experts. However, that expert was able to automatically verify a singly-linked list with just 113 ghost statements for the 90 statements in C. That's more specs than code! Industrial case studies in other article give more examples. More importantly, when making the C code as verifiably safe as Rust, the effort required had developers cranking it out at rate of two lines of code per hour.
Both manual and automated proof with separation logic has a lot more automation since then. Yet, I think programmers will more easily learn to intuitively and heuristically structure their programs to pass Rust's automated, formal checker than learning expert-level C, separation logic, and/or formal proof. And Rust can still do reference counting or unsafe operation in event it can't check something.
This write-up you all keep linking doesn't prove what you think it proves. Unless you're just saying verified handling of mutable state with cycles is extremely difficult in any language with Rust just one example. That would be true. The article also proves Rust ecosystem comes with nice guides on doing that stuff with minimal effort vs what it takes to learn same stuff for C code.
> Both manual and automated proof with separation logic has a lot more automation since then. Yet, I think programmers will more easily learn to intuitively and heuristically structure their programs to pass Rust's automated, formal checker than learning expert-level C, separation logic, and/or formal proof.
Yeah but aren't these formal proof methods a lot more general, and thus more widely applicable? In other words, does Rust provide the right balance between security and complexity?
Rust has what we often call a "lightweight" formal method. These are those with simpler types of specification and/or more automated use. They tend to be quite specialized since that improves automation. The limited properties it proves are why it's so much easier to use than the general methods.
Also note that the problems the borrow checker handles are really hard to do safely in general without overhead. They're hard to test for and debug, too. So, the use of a limited logic with automation just for that makes sense. It's why the other tools for C code were developed, too. Before that, people were using things like SPIN model checker for such things. Again, that looks like overkill to get just a few safety properties vs Rust's built-in method that's simpler.
Not only that, any code examples in rustdoc are treated like tests and get executed during compilation! That ensures that your examples are not obsolete (or at least that they compile).
Didn't realize this was possible with Sphinx (Python) until I saw your comment and did some digging. Here's the relevant documentation if anyone else is curious:
As someone who's only dabbled in Rust (writing a few <500 line projects in a mostly non-idiomatic style), I've enjoyed every minute. "Fighting the borrow checker" gives an extra challenge and keeps things fun when not stressed for time. I'm still not using it seriously "in production," but it's easy to identify with the love of the language other beginners have.
Not if you're behind my corporate proxy, unfortunately. I set the HTTPS_PROXY environment variable properly (which is annoying -- other apps are able to deal with the proxy transparently) so that I stop getting 407 errors, and then I get:
info: caused by: [35] SSL connect error (schannel: next InitializeSecurityContext failed: Unknown error
(0x80092012) - The revocation function was unable to check revocation for the certificate.)
I've tried various fixes from the (still open) github issues: set some config setting to prevent the cert revocation check (only works for cargo, not rustup) and installing the cert from static.github.com (doesn't fix the issue at all).
EDIT: I was actually able to workaround this, finally, based on this github desktop issue[0]. It involves registry hackery and disabling cert validation in SCHANNEL which is not ideal (and I will be reverting it once rust is installed). I certainly don't have the mentioned Russian crypto library installed on my machine, so I have no idea what could be ultimately causing cert revocation check to fail on my machine...
Does your proxy block OCSP requests to LE's servers? LE's OCSP server goes down from time to time and causes that same error for everyone, but if it's happening consistently for you it's more likely to be because of your proxy.
62 comments
[ 2.6 ms ] story [ 104 ms ] threadOver OCaml/Standard ML: more control over performance. It's also imperative first rather than functional first, which isn't neccessarily an advantage, but it is to people coming from a imperative programmig background.
Over D: much more momentum behind the ecosystem.
If you like OCaml/Haskell/etc but find yourself wishing you could make mutability and ownership more safe, or write very fast code more easily, and are willing to give up higher-order type system features for it, consider Rust.
Go is a very different language than Rust even though both superficially resemble C. If you're using Go because you want something C-like but better, consider Rust.
Go is more like (a better) PHP or Java on a webserver.
With its type system and support for generics (in C++: templates), Rust has much more in common with C++ than it does with C.
Wow, I strongly feel the opposite about C. It is hardly simple or clear - understanding what your code will do after the compiler's done with it seems almost impossible, reasoning about runtime behavior similarly so. The complexity involved in managing memory, etc, is so significant.
But it is extremely powerful.
I see go as being extremely simple, but also very weak in terms for expressiveness, relying on the runtime for memory and concurrency management.
I agree that Rust has more in common with C++.
I describe C as "simple" in the sense that it's A) not a large language, by specification and B) somewhat by virtue of the former, there's generally a small range of possible implementations of any given problem (which helps C programs have "clarity"). Go is very C-like in that it also has both of these attributes, quite deliberately. C could be considered complex, however, by virtue of the amount of behavior that is unspecified/at the compiler's discretion. In that sense its safety often is difficult to reason about, but C programs themselves are generally easy to reason about. Go luckily doesn't have many opportunities for programs to be "complex" in that way.
C is "powerful" in the sense that it offers programmers deep control over memory. Given that, (effectively) anything that can be implemented in any other language can be implemented in C, with varying levels of difficulty. It isn't "powerful" from the language perspective itself — in terms of expressiveness or the features that it affords programmers — in the same way that C++ or Rust are "powerful".
A lot of the adjectives we use to describe languages aren't particularly well-defined, so some disagreement about them is almost a given. But hopefully that clarifies.
The FFI story seems to be overshadowed, but if you want to use things like Vulkan, DX12, etc... then C FFI performance becomes absolutely critical, and many languages are really bad at it.
You pretty much get interop with every major language for free. I've embedded Rust in Python, Java, C# and WASM/ASMJS pretty much with just that one feature alone.
Also makes the migration story really strong, which I think was largely driven by needing to be integrated into FireFox in a sane way.
https://lobste.rs/s/5pnqdv/why_is_sqlite_coded_c#c_vhdcyt
Think about all the low level resource management: network ports, files, heap memory... That's what rust does better.
Check ATS for a ML-inspired language with some resource semantics closer to rust and stronger control and correctness primitives. http://www.ats-lang.org
https://news.ycombinator.com/item?id=16442743
When I try to learn a new language, I typically want to: (a) read a lot of code people have written in that language and (b) implement algorithms that I understand well, while keeping in an idiomatic style.
When I tried to do this with Rust, I was shocked by how difficult it was to implement certain algorithms (specifically, operations on Linked Lists and Graphs). Maybe I'd do better if I chose a small project to work on, but it disturbs me to know that such elementary problems are so difficult to solve.
- It is certainly difficult
- Strategies and approaches from low-level languages which are at least cosmetically similar will probably be unavailing
- In some (many?) cases, crates (Rental, Petgraph, Spade) exist that already implement them. Their design can be studied
- There's an entire book (http://cglab.ca/~abeinges/blah/too-many-lists/book/) devoted to writing linked lists in Rust. It's highly informative
Without wishing to explicitly criticise your approach to learning Rust, I'll note that for the vast majority of programmers, it's likely to be frustrating for quite a while.
0: https://doc.rust-lang.org/src/alloc/linked_list.rs.html#46-5...
I'm not suggesting this is a good idea, but I'm tired of the linked list argument which kind of implies Rust is much harder than C for an equivalent linked list implementation. If you explicitly choose to lose the extra guarantees, Rust is about as easy as C to write a linked list in.
Working through the Entirely Too Many Linked Lists book in general is super helpful, since it deliberately runs into the cases that are hard.
Implementing a hash table in Rust would probably be fine as a toe dip too. It's that multiple-references-to-each-object part that really burns you in the doubly-linked list case.
These are probably the two best sites for small, exercise training programs in Rust. Once you've gotten through a few of them, you'd probably be ready to build something useful, which might be more interesting.
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.553...
https://ac.els-cdn.com/S0167642313000191/1-s2.0-S01676423130...
Ok, so author needed to know C extremely well (i.e. "language lawyer"), comfortable with separation logic, able to turn informal requirements into specifications, use of ghost code, and have a SMT solver. The solvers often fail on some stuff that requires formal proof by rare experts. However, that expert was able to automatically verify a singly-linked list with just 113 ghost statements for the 90 statements in C. That's more specs than code! Industrial case studies in other article give more examples. More importantly, when making the C code as verifiably safe as Rust, the effort required had developers cranking it out at rate of two lines of code per hour.
Both manual and automated proof with separation logic has a lot more automation since then. Yet, I think programmers will more easily learn to intuitively and heuristically structure their programs to pass Rust's automated, formal checker than learning expert-level C, separation logic, and/or formal proof. And Rust can still do reference counting or unsafe operation in event it can't check something.
This write-up you all keep linking doesn't prove what you think it proves. Unless you're just saying verified handling of mutable state with cycles is extremely difficult in any language with Rust just one example. That would be true. The article also proves Rust ecosystem comes with nice guides on doing that stuff with minimal effort vs what it takes to learn same stuff for C code.
Yeah but aren't these formal proof methods a lot more general, and thus more widely applicable? In other words, does Rust provide the right balance between security and complexity?
Also note that the problems the borrow checker handles are really hard to do safely in general without overhead. They're hard to test for and debug, too. So, the use of a limited logic with automation just for that makes sense. It's why the other tools for C code were developed, too. Before that, people were using things like SPIN model checker for such things. Again, that looks like overkill to get just a few safety properties vs Rust's built-in method that's simpler.
That is a genuinely great idea.
maybe one day domains, limits and code/proofs will be a single entity in source
With rust this is built into the default tools installed by rustup.
> https://docs.python.org/2/library/doctest.html
Write doctests in your docstrings, run python -m doctest
http://docs.sphinxdocs.com/en/latest/step-3.html#step-3-keep...
Not if you're behind my corporate proxy, unfortunately. I set the HTTPS_PROXY environment variable properly (which is annoying -- other apps are able to deal with the proxy transparently) so that I stop getting 407 errors, and then I get:
I've tried various fixes from the (still open) github issues: set some config setting to prevent the cert revocation check (only works for cargo, not rustup) and installing the cert from static.github.com (doesn't fix the issue at all).EDIT: I was actually able to workaround this, finally, based on this github desktop issue[0]. It involves registry hackery and disabling cert validation in SCHANNEL which is not ideal (and I will be reverting it once rust is installed). I certainly don't have the mentioned Russian crypto library installed on my machine, so I have no idea what could be ultimately causing cert revocation check to fail on my machine...
[0] https://github.com/desktop/desktop/issues/3156
www.redox-os.org