Rust 1.17
blog.rust-lang.org
blog.rust-lang.org
First of all what's the point of Rust? OK, it's a systems language, but what does that even mean? You can write a system in any language right? Python is a scripting language and lots of people use it everyday to write systems. What problems is it trying to solve? Also how does it compare to Go? (I haven't used either one or another). It seems to me that both Go and Rust are mainly used by C/C++ folks that want a new shiny language that somehow resemble the syntax or structure or funcionality of the former and at the same time add some modern features (I might be totally wrong by presuming this).
From the perspective of other languages I know a little bit such as Ruby, Python or JS its syntax seems really bloated to me with all those specials symbols. Do you feel more productive writing software in it than in let's say JS? Please keep in mind that I really don't know Rust (nor I've never tried a statically-typed language) and I don't mean to offend anyone.
Thank you guys!
When people refer to systems languages, they usually refer to languages that need to do lower-level interactions with hardware. For example, calling to your graphics card manually rather than using a high-level GUI toolkit. Or mmap'ing memory, or using internal JITs. This means that you need clear C and assembly interoperability and generally want to avoid garbage collection.
Military don't see to have any issue using real time Java to control weapon systems.
Aonix, nowadays part of PTC, has lots of customers. From missile systems to battle cruiser targeting systems.
So apparently Java real time GCs don't have any issue on the battlefield.
Example the NORMANDIE ballistic missile tracking.
http://www.businesswire.com/news/home/20090302005701/en/Thal...
I have other examples, like battleship gun turrets computer aided targeting systems. You don't want the computer to stop for thinking instead of live tracking what those turrets are supposed to hit.
It's not just targeted at C/C++ folks. The Rust team often hears from people who have been pulling their hair out trying to get a particular component written in a scripting language (Ruby, Python, JS) to go fast enough, which entails getting very intimate with the runtime systems for those languages. They're often surprised that they can instead write that core component in Rust, without giving up any developer productivity (or sometimes gaining it, compared to hand-optimizing their code), and get something much faster than what they were able to achieve in a scripting language.
There was a post just the other day on the Rust reddit telling this story: https://www.reddit.com/r/rust/comments/67m4eh/rust_day_two_a..., with a choice quote: "I feel like I discovered a programming super power or something. I mean I did expect it to be faster, but not this much faster this easily!" You can also check out the videos here: http://usehelix.com/resources
Rust also makes it much easier to write reliable software. The best example of this is with multithreaded programming (which is something you might reach for for speed), where Rust gives you guarantees not found in any other major language: you can know for sure that data is owned by one thread and not accessible by others, and the compiler will check this for you. You can know for sure that you only access lock-protected data while holding the lock. Rust will check thread safety for you, and more. You can read more about the concurrency benefits here: http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.ht...
Relative to Go specifically, in addition to the reliability improvements, Rust supports a wider range of "systems" use cases. Unlike Go, it does not require a garbage collector or other runtime system, which makes it very easy to write small Rust components that can be used with a large script, or to write extremely performance-sensitive or low-level code like a browser or game engine, an operating system, or a bare-metal web server. One reason that Dropbox switched to Rust from Go for one of their core components is greater control over memory footprint, which allowed them to reduce their server memory needs by 4x, leading to significant financial savings.
The slogan I've been using for Rust personally is: Confident, Productive Systems Programming. You can write fast code. You can write low-level code. You can do it with high productivity, thanks to the work on modern tooling and ergonomics. But most importantly, you can do all of this fearlessly, because the Rust compiler has your back.
It's a systems language in the respect that you get full control over the resources that are used or not used, and you can reason about exactly what code runs (or check the generated output). You can write a webserver in Python, but unless you are serving a dynamic site implemented in Python, you are likely paying a steep cost for that, at least in respect to a language with much finer control such a C/C++ or Rust (or many others that are in this category).
> Also how does it compare to Go?
My understanding of Go is that it aims to fit a similar niche, if not entirely overlapping. Sort of a C/C++ with some changes to make it more modern/easier to use, some language functionality that makes concurrent programming easier, and memory management to ease the pains of manually dealing with memory. Sort of a hybrid of C and Java, where Java's VM is reduced to be very simple and embedded with every binary you create. At least that's how I understand it, not really having done much in it.
> From the perspective of other languages I know a little bit such as Ruby, Python or JS its syntax seems really bloated to me with all those specials symbols.
In many cases it's hard to compare from a dynamic language to a low-level compiled language. It's sort of like comparing a Cessna to a Fighter Jet. If you just need to get from point A to point B and time isn't too much of a factor, a Cessna is fine, and comfortable, and doesn't require much training or diligence in its use. When you need to get there fast, and you might also need to fight a battle or two, a Cessna is not sufficient, but you might make do if you bolt a few missiles and guns on it if you don't expect much of a fight. That's probably the equivalent of writing your own module or library in C/C++ and calling to it from Python.
The symbols allow for greater control and specification of the problem, and when your goal is already to make a very robust system, are something you might end up needing anyway. Imagine you are telling a friend how to go pick up your drink at Starbucks, and he's unfamiliar with the area. You can give very general directions and trust your friend is smart enough to figure it out, and in many cases they will, after taking some time to reason out the ambiguities they are presented with in the real world. Sometimes your drink comes back cold or wrong. Alternatively, you can write very detailed directions for exactly where to go, what to expect to see, and what to say. This takes time up front, but you have higher assurance that you will get your drink in a timely and correct manner. Having a shared shorthand you and your friend know might make that list of directions a bit hard to read from the outside, but it can add to accuracy and/or reduce the number of steps if it's a well defined shorthand.
> My understanding of Go is that it aims to fit a similar niche
Not really. Go is used to replace Python. Rust is used to replace C/C++.
I think that's fairly simplistic. Go is often used to replace Python, but what it aims for might be slightly more ambitious. You generally don't hear about people or companies replacing services they've written in C or C++ with Python (implementing when nothing already existed, yet, but generally not replacing), but you do hear that with Go, because the performance is close enough that the trade-off makes sense in more situations.
On a scale of 1 = C/C++/low-level and 10 = Python/high-level, I would hazard that Go aims for the 3-8 range and Rust aims for the 1-5 range. Quite a bit of overlap, but they each go a bit farther in certain directions that makes them a bit closer of a fit in those areas.
With all the power that a systems language gives, comes great risk. To err is human, and most system languages do not prevent humans from erring in ways that can cause significant undefined effects. The usual suspect is, of course, memory safety, but this can also mean things like losing sight of an algorithm because you're carrying too much intellectual overhead when doing the aforementioned memory or system state manipulation. Computer science has lots of very well-studied approaches to solving particular parts of problems, but there's a gap between those approaches and the expressiveness of systems languages to implement them in a way that is fault-free, or doesn't make things like Hoare analysis too difficult to manage.
I am a neophyte using Rust, but it has me excited because it closes that gap in a really elegant way. I just published a libnss plugin that queries Azure Active Directory for user information[1]. Without going into too much detail, it's a set of functions that takes pointers to memory, and stuffs them full of data that it gets from an OAuth2-protected REST endpoint. That's a looooot of "range" in terms of function, and the surface area for bugs in a language like C is huge. On the other hand, doing FFI in runtime-based languages is usually very hard and can be relatively slow, which matters in something as basic as user information retrieval. Rust made writing it extraordinarily easy. I have to "speak C" on one end, but I can handle HTTP calls with the ease and low-risk usually only found in languages like Python. At the same time, if there is a bug that effects memory, I can trust the compiler will catch it, which frees me up to think about structure, maintainability, extensibility, &c.
There are lots of other examples.
Go is sometimes called a systems language, but in a much narrower sense than Rust. For example, it is garbage collected and has a runtime, so it's not great at being FFI'd into. It seems to be quite useful for distributed applications.
Rust, on the other hand, is closer to C++ in that it has no real runtime and no GC. On top of that, it brings compile-time memory safety and lots of other modern features to systems programming. One of its biggest applications is making Firefox safer and faster as part of the Quantum project (https://wiki.mozilla.org/Quantum).
Part of why Rust is more symbol-heavy than Python or Javascript is it has to express things in more detail, giving you the control required to do systems programming. So it's not necessarily a direct competitor to JS, since it can work in areas JS simply can't.
In some ways, though, that can make it more productive- static typing is like compiler-enforced documentation and tests, making it easier to keep track of a large codebase.
I think that's it in a nutshell.
I want to write a program in a language that allows modern programming construction... Cool: Python, Ruby, JavaScript (ES 6+), Haskell, Elixir
The language must be fast... OK, so maybe Java or C#?
I need to control the response times and can't have GC pauses. It needs to be really fast... Assembly!
Stop... OK, OK, you're kind of stuck with C or C++.
Sigh. I want a modern language that compiles to bare metal, doggone it. What should I do?
This is what Rust is. Memory safety without GC, and a modern language without an interpreter or runtime. Plus all the package management/build tool/library ecosystems that all those 1990s languages have. (Would you believe that Java, Ruby, and Python are all more than 20 years old and that Java is the youngest of the three?!?)
* A faster grep ( https://github.com/BurntSushi/ripgrep )
* A GPU accelerated terminal emulator ( https://github.com/jwilm/alacritty )
* A web browser ( https://servo.org/ )
* A containerization system ( https://github.com/tailhook/vagga )
* An operating system ( https://github.com/redox-os/ )
* An extremely fast text editor ( https://github.com/google/xi-editor )
And be faster and safer than C/C++.
Rust is a modern C/C++ replacement, or at least tries to be. That's no mean feat.
With that said, it's a very interesting project. Especially with regards to its rendering engine.
Those systems failed market adoption mostly due to politics and company acquisitions than technical hurdles.
Modula-3 is a good example of how to support all scenarios required by a systems programming language with GC, unfortunately Compaq buying DEC followed by HP buying Compaq, killed all the work the SRC group was doing.
For embedded work check the Oberon compilers sold by Astrobe for ARM Cortex-M3, Cortex-M4 and Cortex-M7, as well as, Xilinx FPGA Systems.
Rust should be beating the majority of those languages in well-implemented comparisons.
Refcounting is often one of the slower ways to implement a GC. It also has other issues, like long GC pauses when a large structure goes out of scope.
For fairness you need to compare Rust to D or Pony or SBCL, which are also close to C++ (even faster), plus added concurrency safety or memory safety, which can be circumvented in Rust.
In idiomatic Rust code, you typically have very few reference increments/decrements, making it faster and more efficient than both, GC and traditional RC-based approaches.
The reasons for this are that objects are often allocated on the stack, passed by reference (a safe pointer), and directly integrated into a larger structure, requiring fewer heap allocations and very few – if any – refcounted objects. In Rust, unlike C or C++, you can do this safely and ergonomically because Rust enforces clear and well-defined ownership semantics.
https://dlang.org/spec/memory-safe-d.html
https://dlang.org/blog/2016/09/28/how-to-write-trusted-code-...
http://sbcl.org/manual/index.html#Declarations-as-Assertions
To witness, https://github.com/ggreer/the_silver_searcher (aka "ag") is about as fast as ripgrep, but written in C.
This is not to say that Rust does not have benefits. But the benefit is not "speed", but "speed plus security".
But nobody said otherwise? I don't understand your point. Speed + safety is indeed precisely the point. I would implore you to do your own comparative analysis by looking at the types of bugs reported for these search tools. (I can't do this for you. If I could, I would.)
> What makes ripgrep fast (AFAIK) is mainly using mmap() instead of open()/read() to read files,
I think you're confused. This is what the author of the silver searcher has claimed for a long time, but with ripgrep, it's actually precisely the opposite. When searching a large directory of files, memory mapping them has so much overhead that reading files into intermediate fixed size buffers is actually faster. Memory maps can occasionally be faster, but only when the size of the file is large enough to overcome the overhead. A code repository has many many small files, so memory maps do worse. (N.B. My context here is Linux. This doesn't necessarily apply to other operating systems.)
> and relying on Rust's regex library that compiles regexes to DFAs which can run in linear time.
There's no confusion that such things can't be done in C. GNU grep also uses a lazy DFA, for example, and is written in C.
The "linear time" aspect doesn't show up too often, and none of my benchmarks[1] actually exploit that.
There's a lot more to the story of how ripgrep beats the silver searcher. "About as fast" is fairly accurate in many cases, but to stop there would be pretty sad because you'd miss out on other cool things like:
- SIMD for multiple pattern matching
- Heuristics for improving usage of memchr
- Parallel directory iterator (all safe Rust code)
- Fast multiple glob matching
In many cases, this can make a big difference. Try searching the MySQL server repository, for example, and you'll find that the silver searcher isn't "about as fast" as ripgrep. (Hint: Take a peek at its .gitignore file.[2] This has nothing to do with memory maps, SIMD or linear time regex engines.)And yes, I could have done all of this in C. But it's likely I would have given up long before I finished.
[1] - http://blog.burntsushi.net/ripgrep/
[2] - https://github.com/mysql/mysql-server/blob/5.7/.gitignore
Other non-garbage collected languages (ie those with manual memory management) lack Rust's memory safety semantics and are this subject to segfaults, buffer overflow exploits, etc. Rust is extremely "safe" since it prevents these types of errors at compile time.
Clang is a competitor to GCC.
If I understand, no languages offer the same assurances, I remember GodBolt is a nice way to explore how it's compile to assembly code you can compare.
https://rust.godbolt.org https://gcc.godbolt.org https://go.godbolt.org
[1] Many compilers have much more than two stages. For example, Rust has another intermediate representation called MIR.
Rust's main benefit is in the compiler itself, not optimization and codegen.
(However, you're correct in that it's not undefined behavior to mess up locking in Rust, at least not without an `unsafe` block involved.)
You know all this, of course. I'm just commenting for others' sake.
Rust is like the safety mechanism on a sawblade that shuts off once it realizes it's cutting into your finger.
It's just like typed and untyped languages. Typed languages require more up front work in that you must define all the types and data structures and which functions can accept them. This is more work than just creating them ad-hoc and using them as needed, but it prevents certain types of errors by catching them at compile time. The ownership and lifetime information for variables is loosely equivalent to that. It prevents certain types of problematic usage. It isn't perfect, and sometimes you have to work around its limitations, but the same could be said of most type systems.
There are plenty of primers on this feature of Rust, I advise you to take a look, you might find it very interesting.
I wouldn't recommend anyone write real code using LLVM IR, but it's not as bad as you'd expect.
It's not a set law, but more expressive type systems almost always increase the class of properties that can be "easily" proved in a language. I work on a verification tool for C/C++ programs and we constantly struggle with the languages. Pointer arithmetic and aliasing dramatically complicate any possible analysis, and these problems are only exacerbated at a lower level IR/ASM level.
Node.js, Java, Go use Garbage Collection for use case where programmers do not have to manage memory.
If Node/Java/Go use GC (or VMs), then aren't they more safe than Rust?
Nope, it's largely depend on the language design.
> If Node/Java/Go use GC (or VMs), then aren't they more safe than Rust? Yes and No, memory allocation is one of the issue in GC. Go is generally more safe on networking but not security where Rust can manage it securely.
This is a good read on Java vs Rust: https://llogiq.github.io/2016/02/28/java-rust.htmlhttps://news.ycombinator.com/item?id=14173716 https://github.com/stouset/secrets/tree/stack-secrets
In fact, we don't have to bother much with GC because it's depend on programmers and job availability. GC was created based on the idea that manage memory is hard for large scale project. It work well for Azul Systems, and they have recently advertised for LLVM engineer to bring more performance where JVM could not.
Go has the advantage of memory safety (via GC), plus better concurrency safety, which is lacking in Rust. There are concurrency safe languages, but not mentioned in this thread.
Rust however prevents these kinds of errors at compile time.
Which things were you thinking of that Rust was lacking here?
Much better would be a proper type system to get rid of those at compile-time of course. Look at pony. And a better memory-safety system than RC.
As for Pony,
> Pony-ORCA, yes the killer whale, is based on ideas from ownership and deferred, distributed, weighted reference counting.
That's it's GC.
More technical explanation: https://www.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com...
"In practice, methods are not compiled the first time they are called. For each method, the JVM maintains a call count, which is incremented every time the method is called. The JVM interprets a method until its call count exceeds a JIT compilation threshold. Therefore, often-used methods are compiled soon after the JVM has started, and less-used methods are compiled much later, or not at all. The JIT compilation threshold helps the JVM start quickly and still have improved performance. The threshold has been carefully selected to obtain an optimal balance between startup times and long term performance."
> Go has the advantage of memory safety (via GC), plus better concurrency safety, which is lacking in Rust.
Are there any examples you could list?
One of Go GC issue is based on this stouset's discussion
https://news.ycombinator.com/item?id=14174500This is not borne out by practical experience. While we've not experimented much with Rust, our Java deployments are significantly larger than other native languages like Go (and I expect Rust would actually be a bit smaller than that since it requires less runtime than Go).
JVM deployed binaries are large, not especially because the bytecode is large, but because you have to ship all the bytecode for all your code and all its transitive dependencies; there's no linker and the semantics of the language make it essentially impossible to statically prove that individual functions or classes aren't needed. You can trim it down with tools like Proguard, but that's a non-trivial undertaking and prone to error, which again you won't know until runtime.
Plus the drawback that you need a relatively large VM to run a JVM binary, but you can run Rust binaries completely standalone (out of a scratch container if you want).
> Go has the advantage of memory safety (via GC), plus better concurrency safety, which is lacking in Rust.
I'm curious what you mean by "better concurrency safety". My understanding is that Rust attempts to statically prove that concurrent accesses are safe (e.g. https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h..., especially the section on locks). Go does nothing of the sort - it provides some nice concurrency primitives and tooling to detect data races, but the compiler does nothing to actively prevent it.
Of course you can use some other compiled language instead of Rust, I think the choice boils down to productivity and ecosystem.
EDIT: where I said "some other compiled language" I should have really said "some other compiled and non-garbage-collected language"
What Rust does is track reference lifetimes at compile time, giving you certainty about who can safely "own" or "borrow" memory in every single line of code, without any runtime pointer indirections or other slowdowns. The language is built around this feature at every level, with "lifetimes" being a syntactic construct at the level of type and mutability.
Imagine if you wanted to safely parse user-submitted JSON, maybe cache derived products from that JSON, and then make sure that when the input buffer is released, you weren't holding any handles into strings inside it. The only safe way to do that in any other language is to proactively copy the strings, or rigorously reference-count the input buffer. But Rust has your back here. If you use zero-copy deserialization from Serde ( https://github.com/serde-rs/serde/releases/tag/v1.0.0 ) then the compiler will refuse to compile if you are using any of that data longer than the lifetime of the original buffer, and do so without needing to add any runtime bookkeeping.
Yes, it's an annoying language to learn because of that "fight with the borrow checker." I LOVE that the language designers and steering committee are so open to quality-of-life improvements for newbies, like that string warning. The language will only get easier to learn over time. It may never be what you use to make your next app, but if you're doing systems programming, it's the wave of the future.
Given this, I wonder if the (seemingly) added complexity of Rust could result in more attack surfaces of other kinds.
I don't know anything about Rust mind you.
So it's theoretically possible to express such a program in assembly language, but it's not something humans could realistically produce without tools such as Rust.
Yes, the reason is things like garbage collection and language runtime. Every program does ultimately run some form of machine code, but the amount and type of code generated can vary very widely, not even considering things like VM where you have another couple of layer of abstraction that slow things down.
https://github.com/BurntSushi/ripgrep/blob/master/src/pathut...
I get that it may be hard to read if you're not familiar with the language, but so is * and & if you're not familiar with them in the context of pointers. Sometimes however, a language feature calls for a special symbol as is the case here.
The usage of apostrophes in English also doesn't make much sense for an outsider, but they're very much a necessary part of the language and very easy to use if you're an English speaker. Same applies for Rust.
I'm a beginner to Rust and this was my exact reaction (Too much complex syntax!!), but one conclusion I have come to since, is that one of the really nice thing languages like Python do, is that they just don't deal with a whole bunch of CS issues (eg: everything is a reference to an object) or are very opinionated about it (ownership/lifetime is bound to scope, no way to extend/change it). By doing this, not only are the language simpler, but they also just need less symbols.
One of the ways languages differ is how much of the hardware they abstract. Assembly is literally a list instructions to manipulate registers (a component of a processor) containing 1 number at a time, and to jump around those lines. Resisters and you memory just hold ints, so the programmer has to know what ints are numbers what are addresses pointing to an array or other number(pointer), etc. C abstracts a ton of those instructions, so for example you have to keep track of what memory you're using but not which register has that memory's address and how to allocate registers etc. Static typing is necessary to remember how to interpret those ints. Python abstracts even more so you don't have to keep track of you memory and its very close to abstract logic. However, in exchange there is a large bit of code that has to run with you python code to figure out how to use the registers and assembly instructions to compute your function. OK it doesn't do that directly, but indirectly that's the point of what is called an interpreter.
There are advantages of more abstract languages like python. You don't have to think about as much and there are generally more safety guarantees. However, sometimes you can't afford to the time or space overhead of the interpreter. This is a commonly cited reason people move away from ruby. Sometimes you need the minute control over you data. For example, if you are writing a OS or a device driver, you need to give very specific instructions to the processor. Languages that are well suited for the latter cases are usually called systems languages.
Rust and Go are examples of languages that try to fit between C and Python on this spectrum. They want to give some of the abstractions and security grantees that python provides without the overhead and while enabling some precise control of the hardware if you need it. They also provide some abstractions that may speed up you code because the language designers have thought through some complex algorithms and code better than you may be able to.
Hope that helped, and let me know if you have any questions.
Systems here means things like an operating system, a device driver, video games or a web browser, things that need fast execution, direct access to hardware and the ability to manually maintain memory. Stuff that C/C++ are normally used for.
Could you do it in Python? Not really, since Python has garbage collection, a very high-level of abstraction and is extremely slow for these kinds of tasks.
> What problems is it trying to solve?
It's trying to make it possible to write programs that require manual memory management, but that are checked for correctness of that manual memory management automatically, so that you can avoid many bugs that are related to memory and are commonplace in programs written in C/C++, like referring to a certain object that is in fact no longer present etc.
> Also how does it compare to Go? It seems to me that both Go and Rust are mainly used by C/C++ folks that want a new shiny language that somehow resemble the syntax or structure or funcionality of the former and at the same time add some modern features
Go is not really used by C/C++ programmers, as it does have a garbage collector, just like Python. Unlike Python however it is way faster and unlike Java, it is compiled to a native executable, thus it is used by many Python/Ruby/Java programmers who want a faster/native language with similar capabilities.
Go is mainly used for writing web applications and command line apps, just like Python.
> Do you feel more productive writing software in it than in let's say JS?
Rust targets a very different market from JS, but it has a very strong type system and catches a lot of errors at compile time, which to me makes it a lot more productive than JS is, just by not having things explode on me at runtime.
As for the syntax, it actually feels very neutral once you spend a couple of days with it and read the book + apart from the 'lifetime annotation there aren't really any other weird symbols for the category of language Rust is.
For example things like https://github.com/redox-os/redox or https://github.com/servo/servo would be very hard to write in both Python & Go, because they require direct access to hardware, fastest possible execution and manual memory management. They could've been written in C/C++, but would then contain many memory management errors, (given its manually done by the programmer and programmers are fallible humans), which could lead to crashes or serious security problems. Rust aims to prevent these drawbacks of C/C++, while still maintaining all of their benefits, i.e. speed, manual memory management etc.
Think of Go as a better Python (scripting) or Java (application) language - simpler than Java, faster and with better concurrency than Python. Like Python and Java it's garbage-collected, like Java it's statically weakly typed so can't offer the level of guarantees that Rust can, but possibly faster to develop in as a result. Still too new and thus lacks the extensive libraries of either Java or Python, but growing fast.
It'll also let you do whatever you like if you write `unsafe` code.
The main complaint imho is that despite the many speed improvements, it's still not the fastest of the compilers. But it's getting there!
Instead, the compiler should figure out what needs to be recompiled and do just that. This can mean benefits like not needing to recompile everything because you added a comment to a header file.
What it doesn't prevent is crashes. Rust programs will crash if your program tries to do any of the above, and-or blindly unwrap() Result or Option types.
Now Rust can segfault, etc., just like Java, Node, Go, Ruby, Python, PHP and every other "memory-safe" language when using unsafe or native extensions.
Surely if one programming language can do it, another can?
From my experience though, Rust is the first language that I've had the pleasure to use which offers both the safety guarantees of Java (and more, no data races!) and the low level features of C.
It still leaves me in awe what has been accomplished with Rust, and I've been using it for more than two years now.
No, due to the way the language semantics works.
There are static analyzers that do something like that, MISRA-C, High Integrity C, Frama-C, but you are literally using C with Ada semantics at that point, thus it is almost like another language.
A C compiler that checks all of these things at compile time would no longer be a C compiler.
Compile time guarantees require you to change the language and restrict what is allowed to compile. Rust does just that, it's a different language. You could write an extension to C like Cyclone (or the ISOCPPCore guidelines for C++) that make it safer via compile time checks. You would likely need more annotations and most existing C programs would no longer compile.
(There is the ergonomic benefit of being able to transition from a C codebase to a, say, Cyclone one, though)
To be clear, it was my inexperience with Rust, rather than a the language that was the problem. I found it amusing when I had read a few comments saying that "if it compiles it will run".
You sure about that? You can still get Rust to panic if you try to index a vec past the end...
I'm glad I didn't let the fear of been bashed to keep me away of asking. I'm not sure if I'll ever need to use Rust but it surely has the coolest people around it. :) Thank you guys!
For those who are interested in why leaking memory isn't disallowed by Rusts safety guarantees, have a look at the documentation of the (AFAIK) only safe function in the standard library that leaks memory: https://doc.rust-lang.org/std/mem/fn.forget.html
You can write a system in any language right?
Sure, if you're a kernel developer (or rust user) then you might know what "systems language" means more precisely and that phrase makes it easier for you to communicate with other kernel developers, but it loses its' value as a "distinct meaningful element of speech" (define: word) when the context includes almost anyone else.
Hell, even the definition of "System programming language" on Wikipedia[1] is ambiguous enough that many GC'd or even scripting languages would fit it.
So lets stop using just bare "systems language" to describe rust. It deserves better. Newcomers faceplanting into the 10-story granite wall that is the borrow checker is bad enough, there's no reason they should be confused by the tagline on the homepage.
You can write a system in any language right?
[1]: https://en.wikipedia.org/wiki/System_programming_language
And the definition of race car is ambiguous enough that you can take your 70's pinto onto the track if you want. In some contexts that might even be entirely acceptable. But if you look at the current uses of the term and what it applies to, and why, it becomes fairly obvious what class of vehicles it's referring to. There are, of course, more specific terms (Indy, Nascar, Formula 1, Rally) for more specific meaning. If someone says they are building a race car, you have a pretty good idea that they are building a vehicle to compete in a race, and speed and handling are important, but specific details might change depending on the type of racing.
> Newcomers faceplanting into the 10-story granite wall that is the borrow checker is bad enough, there's no reason they should be confused by the tagline on the homepage.
Then again, should newcomers come under the impression it's as easy as Python or JavaScript and when they hit that brick wall think it's just themselves, or that the language is poorly designed because it isn't as accessible? Systems language is probably the closest thing I can think of that approximates what to expect out of languages in this category. More explanation might be nice, but I think systems language signals the right things for people that know the term, or are willing to look it up.
Right, but there's nothing like that for a "system programming language". What does that tell me? I might assume it meant manual memory management or a language suitable for writing a kernel, but the Go people call that a "system programming language" even though neither of those things applies.
> a general-purpose language designed with systems programming in mind
And I'm not sure this definition is actually unrepresentative.
Contrast this with rust-lang.org where "system" is the fourth word on the homepage. People like to blame Go for people being confused about what "systems language" means, but it seems to me like this is just poor branding on Rust's part.
I don't want to think about the mental gymnastics it would take to come to that conclusion.
[1]: https://blog.golang.org/go-version-1-is-released
[2]: http://web.archive.org/web/20101001012723/http://golang.org/
> What does that tell me? I might assume it meant manual memory management or a language suitable for writing a kernel, but the Go people call that a "system programming language" even though neither of those things applies.
I'm not sure picking one relatively odd member of a category and concluding that since it doesn't match in some aspects that the category doesn't exist is useful. To stay with the theme, you might encounter 24 Hours of LeMons[1] at some point, and I don't think that makes the idea of race cars invalid or useless (there's a reason I said a 70's Pinto might be acceptable).
You can do some systems programming with garbage collected languages, or even dynamic languages in a pinch. It's not ideal in the vast majority of cases, but there are certain situations where it's not entirely unacceptable either (running the webserver in the same language as your dynamic site is implemented in is one).
1: http://www.cnbc.com/2016/06/19/how-an-amateur-launched-a-mil...
Is Go an odd example then? What's a typical example? Better, out of the 10 or 20 most popular programming languages, which are "systems languages" and which are not? I genuinely don't know how to tell, other than maybe "no GC". As far as I can tell the term mainly just means "language I like".
It's self-evident that this is not the case for "systems language", else we wouldn't be talking about this. By itself, "system" means almost nothing and it doesn't help one understand the phrase "systems language" at all without prior knowledge. Take a moment to dwell on the mental context of someone that would say this and what it means to them:
You can write a system in any language right?
> should newcomers come under the impression it's .. easy
Newcomers that don't understand the term at first glance come with no impression because it's ambiguous.
> More explanation might be nice
If you have to reference a footnote to describe the word every time you use it, the word has lost its' value as as word. You might as well call it a "peloozoid programming language" [1]. Just use a better word. Or find a qualifier to tack in front of it to make it more clear. And before you dispute the phrase "every time you use it" I would like to take a second to reference nearly every Rust post on HN and the forums where people are confused by the term "systems"; this theme might be more common than the (tiring) rust-vs-go theme.
This whole paragraph almost comes across as intentionally exclusionary. The Rust community is making huge efforts to make Rust more approachable and ergonomic, especially to non-systems programmers†. This effort can extend past the language itself into the way it describes itself.
I'm not delusional, there's no chance that the 4th word on rust-lang.org will change because I made a comment on HN. But I can wish.
† This is one of the great strengths of Rust! You don't need years of experience with manual memory management to not footgun yourself, Rust has your back. With it's FFI and safety story it's in a great position for dynamic language devs to write extensions for their language with minimal experience and investment.
I understand the point, but not every concept is easily explainable or should be known to everyone as common knowledge. That's wasteful. If someone is confused about a term or how it applies, or if it seems like it's being used in a different context, then they can look it or up ask, like they did here. If I had seen a suggestion that better encapsulated Rust, I would be on board with you in saying it should change, but I haven't seen that yet.
> If you have to reference a footnote to describe the word every time you use it
You don't, unless you are exclusively talking to to people about it that have no industry knowledge or training, and then I think the term is probably either not something they care about or something they should learn about. We don't ask Doctors to change their industry jargon for non doctors, and doctors that don't know it are expected to learn it. Communication is a two way street.
There's a middle ground between making up new words and dumbing down all communication to the simplest words in the language. Neither is particularly useful in the long term. I don't know if Rust has hit a good middle ground, but I do't have a better solution than what I see them doing, and I don't see what I consider better solutions being presented.
Despite Rust looking a lot like C++ and often occupying a similar role as C, it's heritage is just as much that of an ML, and it shows in the semantics and type system. So while a lot of people do use it as a safer C, I often find it to be a "pragmatic Haskell".
My company does much of our back-end in Rust, and we've found it to be an excellent application language. A lot of this is the type system and the tooling. Cargo, the package manager and build system is excellent, builds are far more reproducible and I've spent far less time debugging builds (as opposed to code) than just about any other language I've used. Rust's type system is great if you like that kind of thing, and I find Rust to be particularly easy to debug and refactor. RAII and destructors/drop semantics come in handy more often than you'd think, and the "fearless concurrency" thing is real.
That said, lifetimes do occasionally cause some pain, the learning curve isn't the kindest, and the patterns are different from a lot of other languages. Personally though, I've found Rust to be very productive after getting over the initial learning hump and understanding the borrow checker more. This is gets more true the larger the project gets, as Rust tends (in my experience) toward the trade off of putting a fair bit of complexity up front in exchange for a slower accumulation of complexity as the project increases in size and scope. It's a trade off that I'm often happy to make, but is always a judgement call.
This makes large refactorings so much easier. It also makes working on large projects with lots of people much easier.
In the same way that type information can be used by the compiler to optimize out some operations, the type information and the variable lifetime and ownership information/rules allow for specific optimizations that are harder to assume in C.
That is, you can do the exact same operations in C and Rust, and because you were forced to define more constraints in Rust (or defaults were inferred without you defining them), the compiler can make stronger assertions about what is being done, so it allows for the possibility of more optimizations.
Joking aside, I'm not arguing that the Rust compiler is "better" than any other compiler. The Rust team seems to have done a great job with it, but I'm neither a PLT guy nor a compiler writer so I can't profess an informed opinion there. What I am interested in though is the Rust language semantics (which the compiler enforces, just as every correct compiler enforces the semantics of the language it compiles). Rust-the-language provides benefits that we (my company's engineering team) finds to be valuable for writing our software -- a strong, flexible, static type system; Option/Result types; the ownership model that promotes good practices and prevents a lot of concurrency bugs that a GC would not help with; drop semantics which allow interesting and powerful techniques with smart pointers. As a bonus, Rust-the-ecosystem includes some excellent and ever growing tooling that lets us focus more on solving development and business problems.
As for Rust-the-compiler, others have already done a better job than I think I can, so I'll let those comments speak to the issue.
Other people accomplish great things with other tools. Rust works really well for us though, and I like to share that.
You'd want to check out https://tokio.rs/ and the (released very soon, but not quite yet!) hyper 0.11 that integrates with it.
As someone who likes neither dynamic typing or Ruby's syntax, I do agree that Elixir looks very cool, and have considered it for jobs that favor development speed over compile-time correctness.
Our APIs are JSON and Avro based, if that's what you're asking.
The company does two major things: connect to sensor systems and collect data, and provide ML/Data Science/(other buzzwords for math) tools for clinical trials and medical studies. In many ways this means Rust is a "reasonable" but not necessarily "ideal" fit -- we could certainly afford the overhead of a GC, so the no-GC aspect of Rust doesn't really buy us anything major, though it does provide some nice performance and memory usage in the parts below us in the stack (Iron/Hyper, soon Tokio). That said, the more we get into the "big data" and ML stuff, the more value there is in having a good C FFI and good performance profiles.
We get a lot of value from the other aspects of Rust though. Traits (typeclasses) are powerful and flexible. More importantly, they promote that old, but good, Java ideal of "programming to interfaces" without all the other baggage. Rust's support for FP is quite good (though the lack of HKTs means there's no monads, at least not "actual" monads ala the Haskell Monad typeclass). The fact that Rust closures are actually traits means you can make some really smooth interfaces that transition from just a static function to a closure and finally to a 'proper' object.
The Macros 1.1 release recently and the Serde library hitting 1.0 have been a big deal. JSON APIs are trivial to write and get all the benefits of strong typing and Option/Result on the deserialization side.
One mistake I think people make with Rust is thinking that because you can do things in a really efficient style, you should. This leads to thinking that Rust isn't as good for "applications" work because you have to think about heap vs. stack allocations and lifetimes and such. But if you were going to use a GC, you can probably afford dynamic dispatch and some extra allocations anyway. Relax, clone the data/box the trait, and come back later if the performance matters. If you wrote the thing in Python, you weren't going to get a choice about boxing things and optimizing would be way harder anyway. Basically, if you treat Rust like an impure (and strict) Haskell or OCaml/F#, it'll actually work pretty well once you learn some idiosyncrasies.
Sorry, bit of a long tangent, but yes, like I said in the last comment, Rust asks a little more up front and in return you get better stability, correctness, and refactoring ability. Testing is easy, especially with unit testing built into the compiler flags and Cargo. Cargo really helps with the "works on my machine" issue and deployments. Pulling in libraries is painless, and while the ecosystem is still small compared to (for example) Java, it's growing, enthusiastic, and so far has covered what we need most of the time.
It's not always clear cut though. I think Go is a great language for writing system tools, and yet it has a gc.
One of the successful stories with Rust I read https://users.rust-lang.org/t/success-story-new-rustacean-be...
In other blog, there are benchmarks that Go outperform Rust but I believe the Rust code didn't use buffer read. http://126kr.com/article/5cikidl5bhe
https://www.youtube.com/watch?v=WQbg6ZMQJvQ
It's using somewhat historic version of Rust, but ideas explained are quite applicable.
So why am I interested in rust? Well if you read around a bit in the literature, you'll find about a million ways to shoot yourself in the foot with C. I have been bitten by that in practice, with subtle memory allocation errors in C code I had to use, that manifested non-deterministiaclly and that I failed to debug without help.
I am very interested in a language that allows me to write fast compiled code, and do so safely, exactly because I'm not a C wizard or systems programmer who has a lot of experience writing mostly bug free code. I don't _like_ writing C code, it feels brittle. In python the python interpreter takes care of everything for me. When I go down to C (as cython or weave allow me to do) things become brittle. (And numba, while I love it and use it alot, is an exercise in constrained writing). Rust is not brittle.
In Rust, mathematics does for me what the python interpreter does on the python side. That's massive, and it's elegant.
It probably helps that I have written more papers including categories than programs involving C though.
With, say, a 12 week cycle instead of 6 weeks, I'm waiting 3 months for My feature to land in the next release, and I feel a lot more pressure to get it done now than I would with a 6-week cycle where missing a release isn't a big deal.
Assuming the team has the release process we'll automated and cutting a release doesn't add a lot of work, then there's not much to lose with this cycle.
having stable release with no regression > releasing fast
Well, unless you made a huge regression that's killing your users... which ironically is a recurring issue alleviated by releasing slower and testing better, not by releasing faster :D
See, for example, this recent article on why web browsers switched to a more rapid release schedule: https://arstechnica.com/information-technology/2017/04/mozil...
Empirically, Rust has done a pretty good job of shipping fast and not releasing regressions, so it seems like this isn't mutually exclusive.
That's actually somewhat a meme within Rust, at least as far as my impression from the outside goes. There apparently were times where most interesting crates required some unfinished feature and thus a fairly recent nightly build of the compiler and/or Cargo. I believe that problem has decreased in the meantime, but as I said, I'm looking into Rust from the outside only and don't have much own experience.
[0]: https://blog.rust-lang.org/2016/06/30/State-of-Rust-Survey-2...
I don't know with what geniuses you work with, but the team I workin are generally incapable of something remotly close. Or maybe I'm just a bad programmer amazed at an ordinary day of work.
I don't really know Rust, but isn't that weird? In most languages I know, I would expect for example Vector3(1, 2, 3) + Vector3(2, 3, 4) to return a brand new Vector3(3, 5, 7), with Vector3(1, 2, 3) unmodified? Unless I do vector1 += Vector3(2, 3,4), at which point I expect vector1 to be modified without creating a new vector?
Why doesn't rust create a new String and concatenates using this new owned String? I guess that's why beginners get it wrong - I would
Pretty much the same, string literals are of type &'static str, which is a string slice with a static lifetime. Since literals are not `mut`, you cannot mutate them.
Back when I did more embedded stuff we cared since it could increase your runtime memory by a non-trival amount depending on what the platform did.
The auto copy to the heap behavior is controversial, desired by many for ergonomic reasons, disliked by others for implicit heap allocation. We'll see if it gets added in the future or not!
Thanks for the details, it's appreciated.
If you want to get the semantics of other languages, just clone the left-hand side with ".clone()".
Isn't that true for every language?
No offense intended, but this strongly reminds me of the "Sufficiently Smart Compiler" [1]
The Java community had more than enough time to implement that, especially compared to a young language like Rust, so there's probably a reason they don't have it yet.
javac compiles chained string concatenation to a sequence of StringBuilder calls, and HotSpot knows how to specifically optimize those:
https://groups.google.com/forum/m/#!topic/jvm-languages/KtVB...
That said, I'm sure there are common cases it doesn't catch.
Closest real world example to a sufficiently smart compiler is GHC, and that can be plenty dumb at times.
Here, SQL can also be viewed as a lazy evaulated programming language, due to its declarative nature and the fact that the execution order is not defined in the code, but instead an (hopefully optimal) execution order is determined by the query planner.
Which, string handling being specifically optimised by the compiler, is exactly what was being discussed upthread?
We could make the addition operator implicitly allocate here. It's a bit of a philosophical question if implicit allocations like that are desirable in rust. I lean towards being okay with the implicit allocation.
In other words, it would violate the conventions, but not the rules.
(Okay so there's a _really_ obscure rule here that sill prevents this but it only matters because it's the standard library. It's not actually the signature)
fn main () {
let hello = "Hello".to_owned();
let world = "world";
let hello_world = hello + world;
print!("{}, {}", hello_world, hello)
}
4 | let hello_world = hello + world;
| ----- value moved here
5 | print!("{}, {}", hello_world, hello)
| ^^^^^ value used here after move
= note: move occurs because `hello` has type `std::string::String`, which does not implement the `Copy` trait
The docs are also pretty awesome as well [0]. They also point out most of this and list out all the types which implement the copy trait.> Copies happen implicitly, for example as part of an assignment y = x. The behavior of Copy is not overloadable; it is always a simple bit-wise copy.
> Cloning is an explicit action, x.clone(). The implementation of Clone can provide any type-specific behavior necessary to duplicate values safely. For example, the implementation of Clone for String needs to copy the pointed-to string buffer in the heap. A simple bitwise copy of String values would merely copy the pointer, leading to a double free down the line. For this reason, String is Clone but not Copy.
> Clone is a supertrait of Copy, so everything which is Copy must also implement Clone. If a type is Copy then its Clone implementation need only return *self (see the example above).
I haven't really used Rust at all, but the more I look at it the more I want too. I probably will not be given the opportunity anytime soon though. Such is life in the world of frontend development.
a man can dream.
The part beginners tend to get wrong is trying to add a &str to a &str, which just doesn't have a + implementation to begin with, so they get lost.
In a systems language, allocations matter. As such, people are generally really against hidden allocations in Rust, moreso than in other languages. This totally could have been done, but by requiring a String on the left, you are already in "this is heap allocated" land, and that is much more palpatable for Rust's audience.
=palpable + palatable. Pedantry aside though, I like the word. Maybe it describes something can be palpably palatable.
https://www.reddit.com/r/rust/comments/67rjii/question_about...
The basic problem is that str slices exist in the "core" library, which can be used on targets without an allocator (for example bare-metal embedded systems), while the owned String type exists only in the "std" library, which requires an allocator. Rust has rules that prevent one library from stomping on another library's types in certain ways that could conflict with future API additions, and those apply to the standard libraries here. (One solution would be to allow the author of libraries A and B to declare that A is allowed to treat types from B as though they were its own.)
Turns out my assumption (like C++, make expensive things more verbose) isn't the complete picture. The big blocker is a technical issue preventing `"" + ""`
See https://github.com/rust-lang/rust/issues/39018#issuecomment-...
That have chosen safety first and appear to be working on ergonomics without sacrificing that.
Pretty much all of the ergonomics improvements this year have been improving on things you can't do in C to begin with. Like automatic serialization (serde). Or const generics. Or error handling via ?. Or pretty much any reasonable string manipulation ever.
There's a legitimate debate to be had with the ergonomics of C++ vs. Rust (though I think Rust wins there too). But not with C. C, as a language that makes you jump through hoops to even get a hash table, doesn't even come close.
I think c++ without inheritance would be a very nice language.
With all these years the amount of UB, memory corruption issues and security exploits only got bigger.
We need a major IoT meltdown to get rid of it, unfortunately it won't happen until the industry lets UNIX clones go.
Redox is a Unix-like system.
UNIX is married with C by design, just how browsers are with JavaScript.
Actually you are right in one thing, if Redox offers POSIX support then it needs to sandbox them.
There is a big difference in having POSIX support and being a UNIX.
Windows, IBM i, IBM z/OS, Unysis MCA ClearPath, Green Hills INTEGRITY OS, Genode and many other OSes have POSIX support, yet their architectures have nothing to do with UNIX.
Rust isn't going to stop IoT makers from leaving ports open and default passwords set.
Bad defaults is a problem with IoT devices, but far from the only problem.
You don't even need Rust, Algol 68 would be enough.
But single inheritance, used judiciously, has really led to some vastly better designs in my experience and doing away with inheritance would generally mean those designs would just be redone with a clumsy "let's fake up inheritance" interface.
I think you can write bad code in any language. Bad C++ is especially egregious, as it's possible to make the bad code practically incomprehensible even at a surface level. However, I suspect that carving feature after feature away from C++ leaves a language that is now just as good for writing toy programs ("hey, look how elegant the language is now that pesky inheritance/template/exception/<insert hated feature of choice> is gone"), but will now allow you write verbose and shitty (but shallowly more understandable) code in place of the C++.
I do find the sheer size of C++ objectionable (the usual arguments about how nearly all C++ programmers seem to program in subset "house styles" of the language). I am generally very suspicious of efforts to replace C++ that come from people who don't seem to understand it at all, however. Generally I like Rust in this regard as the Rust approach seems to display a mature understanding of what was actually good about C++ (constrast: Go).
It's almost like Chuck Moore coming on HN and saying he finds Forth to be the most productive language. Of course it is, for you. (Nothing against the productivity of Forth or Rust.)
Rust is quite amazing, though. Very slick, to the point where I use it and I don't care that much about things running fast or with low memory requirements, I just want something with the syntax and comprehensibility of python/ruby/etc (at least compared to erlang/elixir, which I'd otherwise use -- not having local mutable variables makes things a bit more puzzling for no reason like three times a day) and the sweet tools from haskell, and Rust fits the bill.
The compiler will catch many errors that other languages consider logic errors; if your source code compiles, it tends to run as expected.
As for the disclosure: I'm a normal software programmer, and haven't written a single line of code for rust's compiler and tooling.
This can be solved with a templating system, not a new language.
Which ergonomic issues are your main concerns?
Some things I can think of
- Libraries focusing on optimal performance over ergonomics
- Lifetimes
- Results not being quite as ergonomic as exceptions
While it has rough spots now, I'm hopeful for the future.
The impression I've had watching Rust's evolution is they want to push the language to its limits, find the weak points, find ways of fixing it, and repeat. They don't want to speculatively throw in features. For example, we started off with try!() and later got .?.
Improving the documentation would also improve people's perceptions. I've gone through waves of feeling like Rust's error handling is close enough to the ergonomics of exceptions. When I had surface knowledge, it seemed great. I then started using it and running into problems. I then came across random blog posts and it became easy again.
For a further discussion on documentation, see https://www.reddit.com/r/rust/comments/67fb8e/rust_tribal_kn...
The next step is creating ergonomic libraries. ezstring is an example of people experimenting with this.
For my first large project in Rust, I ran into several issues where the type system was complaining that I couldn't take the references of certain variables in certain ways. Most of the time, it was clear to me (eventually) that what I wanted to do was actually unsafe. It takes some time, but a lot of the pain involved with starting out with Rust is realizing that the patterns you've been using all along in C/C++ were fundamentally unsafe from the beginning.
The rest of the time, though, what I wanted to do was safe, but I couldn't figure out how to structure the code to get Rust in a position where it could guarantee that safety.
What I mean is, it's not the closures exactly that are the problem. It's just that closures are one of the times it would be really really convenient if you could have multiple mutable references to something. You end up having to use `Rc<RefCell<>>` as described here:
http://gtk-rs.org/tuto/closures#using-non-gtk-rs-object-into...
This is also mentioned in the Tock PDF: https://sing.stanford.edu/site/publications/levy-plos15-tock... in section 3.2. I'm not really sure how they overcame that in Tock. I guess using RefCell?
Anyway it all seems very awkward.
Well, there you go. It's an alternative to C. Those tend to get messy (see also, C++).
Perhaps what we really need for the engineering that really calls for this class of product (a systems language), is not more ergonomics, but an acceptance that some small sacrifice in ergonomics and possibly productivity is a small price to pay to make some real headway in the problem of actually providing correct software? Cutting of entire classes of errors and subsequent exploits is an enormous gain for such a small price.
Put another way, every bug you allow through that could have been found with more diligence is really you offloading development time, effort or cost onto your users. That's not to say unlimited time should be spent during development to find bugs, but there is a trade-off happening, and I would hazard that most developers spend less time than they should. In these terms, I think Rust is an amazingly productive language regardless of whether it seems harder to use and makes you feel somewhat less productive.
let foo:'static str = "foo";
Whenever I see an apostrophe, my mind defaults to frantically searching for its counterpart at the end of the string they're enclosing. Just like for parentheses; imagine how confusing it would be if ' would have been ( instead. let foo:(static str = "foo";In fairness, you should never be searching for matching apostrophe. Single quote, maybe. The fact that it's an apostrophe should also trigger that it might have something to do with possession... (or that something was omitted).
For what it's worth, there are many cases even in the natural language where ' is not supposed to have a "closing" counterpart - indeed, the "it's" in this very sentence is one such example. Then you have the use of ' to denote feet and minutes of arc. Now, technically these are all actually different symbols, with their own Unicode codepoints etc - but in practice, people use ' for all of them, and it doesn't seem to be confusing anyone. You could treat the ' in Rust as something along these lines.
There are certainly some things that should just be obvious now, though. Using the C++ namespace separator of :: is just ludicrously obtuse for an extremely common symbol, using the & for refs is archaic (I mean... you could have just called it a ref. ref T instead of &T. And then just ref(foo) or foo.ref to get a ref to foo.) And the syntax for lifetimes is horrible, with the 'a and the fact that in practice its about as significant as naming parameters in a C++ function definition in a header (ie, pointless - the words are discarded by the compiler every time).
And I certainly would have argued for whitespace significance, at least as an option, instead of classic semicolon spam.
Given all that, Rust is still, IMO, the best programming language we have right now for native performance, period. We can criticize what we don't like but we should at least agree its the best we have.
Obtuse? It's sort of common. Multiple languages use it. Personally I like the namespace separator being distinct from the object/struct element accessor. At least they didn't copy PHP in using backspace...
> using the & for refs is archaic (I mean... you could have just called it a ref. ref T instead of &T. And then just ref(foo) or foo.ref to get a ref to foo.)
Please, let's not have more cases where things look like functions but aren't. Functions should look like functions. Operators should look like operators. You might have to learn a small set of operators for a new language. This is front-loading some confusion and learning pain for ease of use later. It may make the language harder to read for people that don't know the language, but anyone reading the source code with the aim to make some changes should probably know the operators at least. I'm not sure giving it a slightly more expressive name but making it look like something else actually makes life easier for those people.
> And I certainly would have argued for whitespace significance, at least as an option, instead of classic semicolon spam.
Optional whitespace significance? That sounds like a recipe for disaster, IMO. I can see a case for one or the other, but mixing of modes like that when it deals with something that is by nature somewhat invisible (or meta), seems prone to problems.
Also, examining why Python has had problems with functional style workhorses like map might shed some light on the negative aspects of whitespace dependent syntax.
use .\greetings\hello;
use ..\foo;
use \bar;
right now these are: use self::greetings::hello;
use super::foo;
use ::bar;
I think the PHP way would have been way betterWhat is wrong with ::? It's visually distinct, isn't easily confused for other things, and unused for anything else, which pretty much hits all the criteria I care about for this feature.
I'm seriously asking, why do people dislike ::? Is there an actual basis, or do people just not like the look?
Also, because of how the metaphor for paths is broken, the relative paths in Rust are not intuitive. That's why you HAVE TO put `self::foo` because the default is not the same as what you'd expect if it were a path.
It's overly complicated compared to just letting you use actual Windows paths.
because how do you parse
let x = a/foo();
is that a::foo() or a / foo()?If you use \ it's unambiguous, a\foo() corresponds to a::foo() while a/foo() is division
So, why again is :: such a bad choice, given all this?
Even for windows paths, backslash has caused a lot of problems in various tools[1][2][3]. Just google "backslash path error" for many pages of problems. Even early DOS developers weren't happy with the need for backslash, and made regular slashes work in paths. That doesn't work everywhere in Windows, but it works in a lot of places. Following Microsoft's mistake because of an accident of history (they has already used forward slash for command flags before implementing path hierarchies so needed a different character) and dealing with the same problems they have is not a point in backslash's favor.
> :: is two characters and is only familiar for C++ programmers
It's familiar for whoever learns it for the language they are learning. That it does not necessarily have an association with concepts from other languages or systems that don't apply cleanly (such as the namespace corresponding to the path, which may not be true) is a positive.
1: https://pythonconquerstheuniverse.wordpress.com/2008/06/04/g...
2: https://github.com/rstudio/shiny/issues/1009
3: http://www-01.ibm.com/support/docview.wss?uid=swg21635233
use foo::bar;
doesn't actually use a relative "path" from the current moduleyou need to use
use self::foo::bar;
I think this is a confusing default, and it would have never ended up this way if Rust used Windows-path-looking namespace syntaxif you're writing a parser and manipulating actual arrays of bytes, you don't have an issue with backslash, it's just another character to you
`&` isn't a parameter annotation in Rust, it's two unary operators, two type annotations (general) and a pattern-matching operator.
The latter of which `ref` also is, incidentally, it means "capture a value by reference" in a pattern: https://play.rust-lang.org/?gist=30b436d83701f74b986aea978fa... (& meaning "capture a reference by its target" in that context, which is logically the structural inverse of the operator)
Of all the glyphs used in the language, :: is probably in the top 5 for frequency of use (probably higher than even * in Rust, considering the infrequency of pointer usage and that multiplication isn't that common an operation in general code) but uses two glyphs instead of one.
It would have required different design constraints starting out to figure which glyph would be best suited instead of ::, but :: is still really ugly in all the languages that use it when its such a common symbol. I wouldn't argue overloading . is right, but I'm having a hard time seeing why a single colon doesn't work - foo:bar:baz. Maybe the parser finds scoping and explicit type names on definitions (ie let foo: i64 or in function definitions) require too many passes to distinguish?
Ugh, no. There are references all over the place when I write Rust code -- it would be a serious bummer for me if annotating those types and making those references took up 4 characters for "ref " instead of just the 1 for &.
Tit for tat. When you gain particular categories of guaranteed correctness there are things you needn't worry about, and that saves you lots of trouble down the road.
How much ergonomical pains such a gain is worth is obviously debatable, but I think it's absolutely reasonable to say that trading guaranteed correctness for comfort more often than not is going to be the right thing to do in systems-level programming. And the lower-level you go, more so.
It's sort of like SVN vs git. When you start you might wonder how can anybody manage to commit a line of code with it, but once you know it, you can't believe you've lived without rebase.
I've been programming in C for 17 years, and in Rust for 2 now. In the first year I had to get help from StackOverflow or IRC for every function I wrote. Now I'm past that, and I'm much much more productive in Rust. I have full control of memory, but no `free()` to write! Strings are easy! Dependencies are easy! I can iterate over BTreeMap with 1 line of code (which inlines to code as efficient as I'd write manually).
It's great, once you know it.
First program:
src/main.rs:65:11: 65:19 error: no method named `join` found for type `collections::vec::Vec<&str>` in the current scope
src/main.rs:65 sline.join("") // return string
OK, what happened to ".join", and who had the great idea of taking it out?Second program:
$ cargo build
An unknown error occurred
To learn more, run the command again with --verbose.
OK, must rerun because of useless message. $ cargo --verbose build
Invalid arguments.
Usage:
cargo <command> [<args>...]
cargo [options]
Note that the "Usage" message doesn't say where to put the options if there's a commmand. So we have to guess: $ cargo build --verbose
Failed to parse registry's information for: chrono
Caused by:
the given version requirement is invalid
The version requirement in the cargo.toml file under [dependencies] is chrono="*"
which, according to this manual [1] is valid.I used to like Rust, but after years of stuff breaking with each new release, I don't use it for anything serious.
Yes, star dependencies were removed like a year ago, for semver issues. They went through multiple release cycles of warnings.
That code worked through at least Rust 1.2. So, yes, Rust has removed an API since 1.0. That code was compiling in December 2016.
Yes, star dependencies were removed like a year ago, for semver issues. They went through multiple release cycles of warnings.
From the Cargo manual [1]:
Wildcard requirements
Wildcard requirements allow for any version where the wildcard is positioned.
*, 1.* and 1.2.* are examples of wildcard requirements.
* := >=0.0.0
1.* := >=1.0.0 <2.0.0
1.2.* := >=1.2.0 <1.3.0
Apparently, nobody bothered to update the manual after removing the feature.This means I don't understand how you got your error. For example:
> cat .\Cargo.toml
[package]
name = "star"
version = "0.1.0"
authors = ["steveklabnik <steve@steveklabnik.com>"]
[dependencies]
semver="*"
> cargo build
Updating registry `https://github.com/rust-lang/crates.io-index`
Compiling semver-parser v0.7.0
Compiling semver v0.6.0
Compiling star v0.1.0 (file:///C:/Users/steve/tmp/star)
Finished dev [unoptimized + debuginfo] target(s) in 5.24 secs
I've filed https://github.com/rust-lang/cargo/issues/3971I'm also not sure how you got your `join` error,
let sline: Vec<&str> = vec![];
sline.join("");
seems to compile just fine for me. Hard to tell without seeing your other code, I guess...It's necessary to run Rust's version of Cargo, not Ubuntu's, when using Rust. Due to a path problem, I was using Ubuntu's older versions of Rust and Cargo, but with libraries from the new Rust installation.
[1] https://users.rust-lang.org/t/crates-io-compatibility-with-o... [2] https://github.com/rust-lang/crates.io/issues/584 [3] https://github.com/rust-lang/cargo/issues/3763
How sure are you that this was compiled with Rust 1.2 and not some old rustc?
Here is a copy of the docs for Vec from June 2016: https://github.com/Manishearth/rust-internals-docs/blob/9419...
There is no join method there.
There is a join method on slices (which vectors dereference to). https://github.com/Manishearth/rust-internals-docs/blob/9419...
This method still exists. It used to take a delimeter argument, and works the same way today. https://play.rust-lang.org/?gist=13d19cd5dd33c1efb046cc69bec...
There have been various language tweaks, but Rust has not removed a (stable) API since 1.0.
I attempted to reproduce your report by downloading the 1.2 compiler and compiling this code in both versions. Here is what I got:
---
First, the Vec.join report. I attempted to compile a crate with this body on both versions:
fn main() {
let v: Vec<&str> = vec!["hello, ", "world"];
v.join("");
}
On 1.2 this failed to compile with exactly the error you stated. This isn't surprising to me since I recall us adding .join to Vec several months after the 1.2 release. So you certainly weren't compiling that code on 1.2.In contrast, on 1.17, this code compiled just fine. This is exactly what I expected, since I use vec.join all the time on vectors of string slices.
In conclusion, I was unable to reproduce this bug.
---
Second, the Cargo star dependencies report. Manishearth is wrong, we disallow those dependencies from being uploaded to crates.io, but end users are allowed to use them just fine (strongly recommended that you don't, though!).
To attempt to reproduce, I built a crate with this dependencies section:
[dependencies]
chrono="*"
On 1.2, I got exactly the error you stated. I don't know what the source of it is, but I know that star dependencies are risky because they imply indefinite forward compatibility, which is impossible to guarantee.On 1.17, this successfully resolved to the 0.3.0 version of chrono, which compiled just fine.
Once again, I was unable to reproduce your bug report.
---
If you have any more bug reports, please post them on https://github.com/rust-lang/rust . And you can check which version of the compiler you're using with `rustc --version` (supported since before 1.0).
.join was implemented using the unstable SliceConcatExt trait. You're kind of asking for things to break if you use unstable things. .join is now implemented on slices, and replaces .connect()
Till now Rust hasn't had that. Is there a decent stack?
The maintainers are great, and welcome contributions.
> When using Cargo’s build scripts, you must set the location of the script in your Cargo.toml. However, the vast majority of people wrote build = "build.rs", using a build.rs file in the root of their project. This convention is now encoded into Cargo, and will be assumed if build.rs exists.
This is what I love about Rust's development. Rather than the developers just using their own ideas about how the Rust ecosystem will use a system, they instead wait for the ecosystem to come up with its own conventions. Once those conventions are organically established, they fold them into the official Rust tools.
It's the equivalent of finding product market fit. More times than not our ideas about what the market (in this case, Rust users) wants will be wrong. Better to just adapt to the market.
That's enough praise for now, so ... how about something not so great about Rust? I started building a password manager in Rust, because I'm not satisfied with the current options for password managers. This will be my first Rust project with a GUI and ... so far it's been very painful.
I've looked at GUI in Rust in the past but never tinkered because there were never any good libraries available. It's now Rust 1.17 and there are still no good options. For this project I landed on gtk-rs, but it's a nightmare of Rc's, RefCells, and borrow_mut just to get even the simplest UI working.
I'm not sure yet if this is an issue endemic to gtk-rs, or if Rc's and RefCells are a necessary evil in Rust whenever GUIs are involved. It's a bit hard to reason about ownership in this case, where your callbacks can be called at any time, and if they modify the UI that may trigger another, now nested callback, which may trigger yet another callback, etc. So it requires a lot of brain power to think about every borrow and what the lifetime should be.
Without thinking too much about it, I feel like things could be better. If Rust was driving the UI loop, and if the callbacks didn't have 'static lifetimes, then it might be possible shove the UI into an isolated struct and not have to deal with too many ownership and lifetime issues. But again, I haven't tried to think it through and it's irrelevant because there's no mature GUI library in Rust that would allow that.
Of course, none of that reflects on Rust itself, nor on the Rust developers. This is a third-party issue; it boils down to a lack of good GUI libraries. That's hardly surprising given how new Rust is.
For now, I'm blundering forward and just hacking together whatever mess of code works. The critical parts of the password manager are in a separate Rust library with unit tests, and the password database will have history/versioning on every entry. So I ultimately don't care if the UI code is ugly and hacked together. It's just more time consuming than it probably has to be.
You also might like https://github.com/antoyo/relm
By the way, I love that Rust devs stalk HN and read my rants :P
I've not delved much into GUI programming in Rust myself, so I'm just interested to hear any arguments for/against. As a more general point, I suspect a good Rust GUI framework will end up looking more in the spirit of React and the FRP approach, as ownership and the tendency towards immutable data have less friction with that approach.
My first attempt was actually with libui, which would give me native components on every platform, but they don't implement a list view which is the heart of a password manager's UI...
> Without thinking too much about it, I feel like things could be better. If Rust was driving the UI loop, and if the callbacks didn't have 'static lifetimes, then it might be possible shove the UI into an isolated struct and not have to deal with too many ownership and lifetime issues.
I recently tried to implement a simple GUI using pure WinAPI, and I finally understod why Rc<Refcell<>> is necessary for callbacks. So far the experience is very mind-boggling.
We desperately need React-like UI library for Rust.
Can somebody point out a list of resources to start on Rust? Do we already have a good IDE with proper autocomplete support?
Thank you!
However, a second edition of the book is in the works and a lot of people have said good things about it. I think enough of it is filled out to be a good starting point: https://doc.rust-lang.org/nightly/book/second-edition/
Since you are in charge of this new language I won't be using it. The Rust community has largely figured out how to get work done with minimal malice and a language that actually exists.