Spec# has annotations and methods for object ownership. You can see an example of it in the "The Spec# Programming System: An Overview" slideshow (slide 39 onwards).
Also:
> C# has two families of data structures that dermine allocation behaviour: structs and classes. If a type is a struct then values of that type will be allocated on the stack:
Here's a primer on how the Microsoft CLR handles it, because it's more subtle than what's stated above. The end effect is that value types behave like values, reference types behave like references. Where they're stored is a function of where they need to be stored, not their type.
Ah yes, my bad - the "value types go on the stack" simplification is a bit sloppy. It's a useful way to describe it to make the comparison - but definitely not for understanding C# in this case.
I like the author's attempt to explain Rust's borrow checker to a new audience by inventing a variant of C#. Lifetimes are certainly one of Rust's most unique features, and the existence of analogies to express unfamiliar concepts in terms of familiar ones is very valuable.
This is offtopic but what should be size of the VM to handle HN's front page traffic. I know a lot of it depends upon whether the website is static or is backed by DB and on various different factors. But many a times some of my friends ask me questions like this.
To make it simple, lets take 2 very basic websites.
1. A ghost blog. (node.js)
2. A wordpress blog backed by a mysql db. (php)
I had a ghost blog on a micro instance of a german VPN, a machine with 256mb of ram and not too fast processing power. Both times I reached HN home page, in one case for some hours, it worked without issues and only a minor slowdown.
I think a lot of people are looking for a replacement for C++ that isn't C#/Java. As in a language that is still low-level enough to offer stuff like pointers, but without the pitfalls and minefield which is C++ (a lot of which is due to historic reasons and backwards compatibility with C).
Some newer languages that try to fill the niche are D, Nim, Rust and Go. Can't say much about Nim, it seems to be in the background. Go started as a systems programming language, but since then they backed out on it and it's mostly pushed as nicer C and webservice language. That leaves D and Rust on the field. Both are competing for the same audience, but Rust has the advantage of a popular brand backing (Mozilla) and a potential killer app (Servo, Mozilla's new work in progress browser engine). It also seems to have a clearer vision because of Mozilla backing and constant dogfooding in Servo. Meanwhile D is more of a design by community, as a result it gets not always the features that are needed or people want, but the features people are willing to work on. Also, D gets a lot of backlash from the game dev and system programming languages communities for using a (not too great) garbage collector, whereas Rust uses memory management somewhat similar to C++ model with RAII/smart pointers, however greatly improved and with much stronger static checks.
I think people just view Rust as the language that could actually be the alternative to C++, because there is a place for a language that is safer to use than C++, yet performs at similar speeds and doesn't handhold you like C#/Java do.
> Some newer languages that try to fill the niche are D, Nim, Rust and Go. Can't say much about Nim, it seems to be in the background.
Nim is frustrating, because to my taste it gets so many things just right while getting one particular thing so spectacularly wrong that I can't bring myself even to try it.
The one thing is its rule for when two identifiers are the same. They are compared case-insensitively, ignoring any underscores and en-dashes in either name, except that the first character must match exactly.
Consequence #1: if you have, say, something called user_sort (perhaps it's a function that sorts users) and something called use_RSort (perhaps it selects which of two sorting algorithms to use somewhere) then they will be the same identifier, with results anywhere from compilation failure to silent malfunction you don't notice until a year later.
Consequence #2: if you want to search for an identifier using some obscure tool like, say, grep or Vim or Eclipse, you're out of luck. (You can make your own tool -- call it "nimgrep", perhaps -- and extensible things like Vim and Eclipse can be given the ability to search for Nim identifiers. But their built-in facilities, like what "*" does in Vim, will do the wrong thing if you are working on Nim code.)
I have used Nim heavily for more than a year now, and it turned out to be the most productive language I have every used. Nim could be considered a modern Lisp with infix notation and Python like syntax with native C performance. The macro system is incredibly well designed and easy to handle.
I also tried Rust, and it turned out to be much more complicated and time consuming. I would use it for systems programming only. Redox is the only reason for me to learn Rust.
> The one thing is its rule for when two identifiers are the same.
First it looked strange to me as well. However there are actually good reasons to avoid case sensitivity.
As for the user_sort and use_rsort example, such semantic errors can easily be fixed by qualification (a.user_sort and b.use_rsort). Errors like the following one however are very hard to fix in case sensitive languages like C++ and probably even Rust:
hmm ... smart_func(a,b) doesn't work. Seems to be a typo. Which function did the author really mean?
Nim would reject such a mess in general.
The developers of Nim have implemented a lot of nice features. A really silly idea however are strong spaces. No serious developer would ever use such a dangerous "feature". It should be removed from Nim.
> Errors like the following one however are very hard to
> fix in case sensitive languages like C++ and probably
> even Rust
No, in Rust the compiler will emit a style warning if you have a function whose name includes capital letters. This can be toggled off (and also elevated into a hard error), but it's always on by default.
Furthermore, when you import a module Rust keeps it namespaced under the module's name, so you never need to worry about names accidentally colliding or being unsure as to where a symbol comes from. You have to use an explicit glob import to pull in all of a module's public items into the current namespace.
Furtherfurthermore, Rust warns when you import a symbol and then don't use it, so even if you name all of these functions differently and ignore the style warnings and glob-import the modules, then you'll get yet another warning when you fail to ever use two of the symbols.
Furtherfurtherfurthermore, Rust doesn't allow symbols with the same name to exist in the same namespace, so even if you went back and fixed all those functions to comply with the style warnings, you'd get compilation errors if you continued to glob-import any of the two of them.
> there are actually good reasons to avoid case sensitivity
My problem with Nim's identifier syntax rules are not about case-sensitivity. I have happily used case-sensitive languages and case-insensitive ones. My problem is with ignoring punctuation in identifiers.
> such semantic errors can easily be fixed by qualification
Once you notice that there's a problem. The trouble is, you might not.
> [three things with names like smart_func but different case]
Again, case-insensitivity is a red herring here as far as I'm concerned.
The idea is that develoers prefer different styles of coding. Some like doThisAction, others prefer do_this_action, others Do_This_Action.
Nim supports all styles. A simple reformatter can be used to transform the code of a project into a normalized form. Such an attempt would be very dangerous in C++.
So the idea is that, say, you'd have hooks in your version control system so that Alice always sees lowercase_with_underscores, Bob always sees camelCaseLikeThis, and Charlie always sees camel_Case_With_Underscores; and then each developer sees a version of the code in which everything is consistent, but it's a different consistent version for each? (And the thing in your actual repository is whatever it is -- it could be w_i_t_h___t_o_o___m_a_n_y___u_n_d_e_r_s_c_o_r_e_s for all anyone need care.)
That's certainly better than having different bits of the code use different identifier styles. But it feels somehow a fragile way to do things. And I worry about encouraging bad habits in teams that don't set things up with such care.
And... can you really make a simple reformatter that will consistently do the right thing? Identifiers may make reference to things whose capitalization shouldn't be canonicalized. HTML. TeX. iPhone. DrMemory.
(Also, though this is a bit of a cheap shot: note that the three identifiers in your first paragraph are not equivalent according to Nim's rules.)
I don't see the advantage in supporting all styles. What you describe sounds like a pretty trivial benefit compared to breaking all of your ordinary text manipulation tools and violating the principle of least surprise.
When I was switching from Pascal (Delphi) to C#, I was wondering why would someone make something and Something two different things. But that what Nim does is definitely weird.
Sure, it's a misfeature, IMO, but it's certainly not a reason to write off the language.
No matter what language you use, if you have two variables named myVariable and my_variable in the same scope, you've created a huge land mine in your code, and your linter should complain. Eventually somebody's going to come along and mix the two up.
The fact that nim blows up differently than every other language just makes it more obvious that this is something you just shouldn't do.
Yup, having myVariable and my_variable is asking for trouble in any language, and what bothers me about Nim is not at all that it makes that harder.
What bothers me about Nim is that (1) it makes it easier for you to call the same variable myVariable in some places and my_variable in others because that won't make compilation fail; (2) once that happens it becomes harder to find references to that variable, because the usual searching tools will miss some of them (and you will not necessarily get any indication that you've missed any); and (3) there are -- hopefully rare, I concede -- cases where what in another language would be obviously and innocuously different identifiers collide. nOpenFiles versus no_pen_files. use_rsync versus user_sync.
And what does this buy you? What is the advantage it confers? That you have the flexibility to call the same thing myVariable in some parts of your code and my_variable in others. But that's a terrible idea and making it possible is not a feature.
> if you have two variables named myVariable and my_variable in the same scope
But that's not the situation in the example. user_sort and use_RSort are perfectly fine identifiers that no reasonable person would expect to be interpreted as the same.
This criticism usually comes from people that do not use Nim.
I was very surprised as well at first - now after 1 year of using Nim I realize I never run into any trouble because of case/underscore insensitivity.
#1: those variables have to be in the same scope or be procs working on the same types to be an issue.
#2: I don't use nimgrep, I just keep a consistent style across my files. When reading somebody else's code, case-insensitive search is usually enough.
I suspect this criticism usually comes from people who don't use Nim because anyone inclined to be bothered by it will not use Nim for that reason.
I agree that "just keep a consistent style" is a good approach for single-person projects. It's harder to keep it working well as you get more people working on the code, though.
Actually Nim's attitude is more suitable for group work because every group can have its own style of coding. A simple transformer could be used to normalize the code. However it would force the developers to qualify all imported conflicting names which is a good idea anyway.
You cannot do this in C++ and Rust. There you actually have to keep a consistent style.
Is there any documentation or discussion on the justification for doing this? I'm scratching my head and asking why anyone would do this intentionally...
Helps make code look more like the algorithm it's implementing - would rather just use α than keep spelling out "alpha" every time. Good editors and REPLs make it easy to enter unicode symbols by tab-completing the latex spelling, e.g. \alpha<tab> gets replaced by α.
I don't use nim, so I couldn't point you to the justification. But as I understand it is so an individual can choose his style preferences. myVariable or my_variable.
There was a C++ developer at job mocking me for using Perl until he realized how much easier data can be parsed with it (and used my Perl code for himself). There are people who despise Lisp just for its parentheses without understanding their purpose which makes Lisp so elegant and powerful. People mock Nim for some things mentioned here without paying attention to the gain of productivity. I am always amazed how banal things can hinder smart people to advance.
Really, the view of a number of bloggers who have used Nim consider it the best among the new crop of languages around for whatever reason, variable naming not withstanding.
Your comment is the only one I have seen making such a big issue about the variable naming.
I logged back into the forums to try and find it, but I didn't see a way to pull up old posts.
IIRC, the issue I posted about had to do with some of the underlying parsers. I was looking at the CSV stuff and I believe one of the types it relies on had a skipBOM function, or one of the functions would skip the BOM, or something along those lines.
But what it did was blindly increment the file pointer without checking it was looking at a BOM, or that it was at the start of the file.
To me the interface is a fairly big deal because you can't safely use the type without knowing that implementation behavior. People do stupid crap, and calling skipBOM at the wrong time will happen, even though it's obviously not intended. What I recall is recommending that it either fail noisily or turn into a noop. I'm on the side of failing noisily to help users of the class find their bugs quicker, but I can understand why someone would want it to be a noop since technically doing nothing if it's not a BOM is the right thing to do.
The response I got was the type wasn't meant to be part of the public interface for the stdlib and so wasn't an issue, and then I was pretty much ignored after that statement. I even offered to do the work myself, but pretty much got ignored.
It wasn't so much that the reaction was negative, as much as it wasn't conducive to improving the stdlib all that much. And from my perspective, one of the biggest problem nim had (at the time) is that the stdlib felt very unpolished. As if someone had written it for very specific use cases and then haven't revisited it. I guess what I'm saying is that it didn't feel like an stdlib in many places, it felt like someone who wrote some production code for very specific use cases. Which is perfectly acceptable for production code with a specific use case, but not for an stdlib (in my opinion).
The entire thing left a bad taste in my mouth and I lost a lot of trust in the quality of the stdlib and so decided to stop learning/using nim.
It's all a little fuzzy in my mind, so I may not have all the details correct but the loss of trust was definitely the result.
And who knows, perhaps I was misreading the code, I wasn't all that experienced with nim at the time. But even having someone point out it was my mistake would have felt better than simply being ignored.
It's too bad really, because I did like the language itself, which is why I had offered to try and help improve the stdlib. Nowadays I'm picking up rust for fun(putting together an NES emulator in it). I'm a little bit of a language whore :)
It seems that the thread you are referring to is this one: http://forum.nim-lang.org/t/1569 (the forum profile view really needs a listing of the person's threads/posts).
From looking at the thread, it seems that this is a simple case of your thread becoming lost in the noise of the forum. Please don't feel that you were ignored on purpose. I think that Araq simply forgot to reply to you, it's easy for threads to get lost like this.
It really sucks, but the fact is that Araq doesn't have enough time to answer everyone and in this case he is the only one who could have assisted you.
In all honesty I think this is quite common in forums. I'm not sure how we can improve it, short of getting full-time Nim devs who ensure that everyone on the forum gets a response. What do you think?
This feels like damage control to me. While your explanation is always possible, that board moved much slower than HN's front page so the idea that the post somehow got lost in the mix when he was actively involved in several other posts sitting on the front page is stretching things a bit (in my opinion).
But that isn't even the important point. The deficiencies I outlined haven't been been revisited have they?
If not, it would seem my distrust wasn't so misplaced, and that was the bigger point.
Who knows, maybe the rest of the stdlib has had more thought put into it, but that was the first bit of the stdlib I had dove into the source for and what I saw wasn't good. I didn't want to do that for the rest of the stdlib either.
If Araq has become the bottleneck, then maybe something needs to change in your process, because as you can clearly see I offered to be the one to make the changes.
And to be clear, I'm not angry about it, I just chose to spend my time elsewhere. Intentional or not, the language dev's as a whole could be doing a better job getting other people involved.
I have done my share of critics regarding Go team decisions, but one thing they got right is that Go is indeed a systems programming language.
My understanding being that a systems programming language is one that can be used to bootstrap itself and build a full OS stack with the exception of some Assembly for interfacing with the underlying hardware.
As of Go 1.6, the language certainly fulfils this description.
If having a GC hinders certain use cases in systems programming, that is an orthogonal issue.
Edit: Looking forward to the day someone bothers to write an Oberon like OS in Go and HNers will keep on downvoting those that favour memory safe systems programming languages.
At this moment, the kernel used is a BSD kernel, but we are working now on making Go run on the bare
hardware and on hypervisors. When that is done, the BSD kernels will be replaced with our own one.
So if that is enough to be a systems programming language, then C# is also one on account of the Singularity OS[2].
I can be made to be one, one still needs to remember Singularity and Midori aren't using pure C#, they have extended the language. Namely Sing# and System C# respectively.
Both projects influenced the design of native .NET on Windows Phone 8, followed by .NET Native, IILC and CoreRT.
Also many of the C# extensions on Midori are being explored for C# 7 and future versions.
C# when compiled to native code is not much different than using Modula-3, in terms of available features. It just needs a little more fine tuning in terms of features.
> Last I checked Go doesn't have any memory placement semantics.
Many OSes like Oberon, Cedar, Topaz, SPIN among others were written in languages without direct support for it, so it is possible to write an OS without it.
The runtime package just needs to offer the services for it, just like many C functions are actually implemented in Assembly.
Theoretically they (or whomever writes an OS in Go) could extend the runtime to add something like `makestack(Type, size IntegerType) Type` to mimic alloca or add in a guarantee that `var b [100]byte` will be allocated on the stack.
(Right now I think it is, but I know there's a Go issue dealing with a function like this, and one of the comments was about potentially creating `a` on the heap and returning an invisible pointer instead of the actual 1e9 bytes.)
"Systems programming language" seems to be one a frequent definition miscommunication, right alongside "a Lisp" and "self taugt programmer".
People use the phrases without acknowledging or without realizing that there is a great breadth (and sometimes controversy) in what people take them to mean. I think you're doing it right defining it where you use it.
You cannot use Go in kernel programming at all without a monumental amount of work. You'd need your own implementation of the language. Go originally did have a runtime that did not require systemcalls and could run on bare hardware, however, this was removed very early on as not being worth the effort.
Yes I do remember that runtime when I was still reading gonuts.
As always, a language and its implementations are not the same thing.
It is nothing special to have a bare runtime that would enable a Go application to be statically compiled and deployed into a Raspberry PI, for example.
It just needs someone to create such Go compiler.
Of course, such work would probably be a nice thesis project for OS design classes.
> And sometimes some people (like me) would argue that C++ (11&14) is actually that language.
The memory safety track records of large-scale software written in modern C++ disagree with you. I don't think this is a tenable argument.
C++11 and C++14 don't really add any memory safety features over C++03 plus user-defined reference counted smart pointers. In fact, I think there's a reasonable argument to be made that C++11 is less safe in practice than this use of C++03. If you use rvalue references and move semantics a lot (since "use-after-move" is now a very real problem), or if you use lambdas a lot (since it's so easy to make dangling upvar references).
Note that it isn't 100% memory safe, it doesn't protect against iterator invalidation (which is a pretty common way in which memory unsafety springs up). But it can be close enough.
Yes we cannot get rid of the C underpinnings, but there are enough features to write safe C++. At least when using it alone or in small teams that value safety.
I have been using it in anger on my side projects for mobile s.
Why use it in spite of my rants about safety?
Tooling.
Using C++ across Android and Windows Phone is already an extra layer of pain (thanks NDK) with first class support from the platform owners.
Using Go, Swift, Rust would increase that pain level quite higher given the actual lack of comparable tooling.
Meaning build scripts, IDE, debugging tools, packaging and APIs.
C# doesn't suffer as much from it thanks to Xamarin, but I would only use it in commercial projects.
I really would wish that one of those languages would eventually be adopted across mobile OS SDKs as C and C++ currently are.
Already had a look at Rust for Windows Phone, but COM support is not yet there to the level expected by WinRT as C++/CX allows. Assuming I was looking at the right place.
> Yes we cannot get rid of the C underpinnings, but there are enough features to write safe C++.
No, there aren't. Empirically, modern C++ projects are not memory safe. Maybe when you qualify it with this:
> At least when using it alone or in small teams that value safety.
But I suspect the reason it looks that way is actually this: Projects that are developed alone or with small teams tend to be less important projects that people don't try to attack and/or find memory safety problems in. The lack of people actively looking for memory safety problems makes it look like C++ projects are safe, but in reality they aren't. If solo/small team projects in C++ had anywhere near as many people looking for holes as big ones like browser engines, then they'd fall over too.
I don't have any hard statistics for this, but given the consistent memory safety track record of C++ projects I think I'm more likely to be right here than wrong. I've written small projects in C++ and I don't think they're memory safe, even though I don't know of any areas where they aren't. I just know what the statistics are.
I fully agree with you, but my point is that until Rust achieves a certain parity with mobile OS C++ tooling the "good enough approach of C++14 and C++17" improvements will prevail, even among those that would rather use something else.
Given myself as example, in spite of all my rants about security that you well know, I still use C++ as my portable business code between mobile platforms.
However I always compile with all warnings as errors, fully embrace C++ STL types for safe programming and do regular static analysis, exactly to minimize as much as possible the errors that might still miss my attention.
If I would be focusing just in a single platform, then I would surely choose between Swift, Java and C# respectively.
Actually, I haven't done much with it, just messing around, so don't take this as absolute or anything but;
Using Rust cross platform, across IOS and andriod, wasn't really that difficult to do...sure, the tooling isn't fully there yet. But I don't think it's any harder than c++, if anything, easier. Cargo is amazing and what makes Rust really nice to use. The community will create better IDE's and an ecosystem of tooling for cross platform, both mobile and desktop OS's. I believe Go also has some fairly popular mobile cross platform libraries as well that are probably much further along than Rusts'.
I can't say for Windows phone as I have never done anything for it and I think it's dead...
With C++ I just download XCode or NDK/Android Studio/Visual Studio.
I don't need to bother with FFI on iOS and Windows.
On Android I just need to bother with JNI FFI, not additionally how to call JNI from the respective language.
The IDEs show visual representation of data structures when debugging and two way editing between C++ and the respective platform language.
With Rust or Go AFAIK I need to use Mac OS X or GNU/Linux for cross-compilation. Need to compile that toolchain myself and write my own wrappers to OS APIs.
Also not enjoying the same IDE experience isn't that much fun.
> I can't say for Windows phone as I have never done anything for it and I think it's dead...
Except the APIs are the same as Windows and XBox are moving to, even if WP10 is at the end of the road now with the last update misfortune.
So if Rust or Go want a place in Windows Store for desktop and XBox apps, they need to get along with WinRT APIs.
Yes, those languages will surely improve but if I have 1 hour to work on a side project, I rather spend that hour coding and not fixing toolchain issues.
If I had more time, I would surely like to help to improve the situation, but sadly time is limited.
I just got Rust compiling for a linux kernel module.
It wasn't too bad, though an inability to read C header files is kinda a pain. And for any target you are going to want to wrap things up in a "Rusty" api, as dealing with cstrings is kinda a pain.
Still, it'd be a pain to go from C strings to C++ strings, and hook new/delete up to kmalloc/kfree.
It guarantees memory safety without a GC. The result is a high performance language that doesn't require manual memory management like C or C++, and generally consumes less memory, as noted by the Dropbox guys. It's an expressive language which is also suitable for systems programming. It's a more modern language than Go (in the sense that is incorporates modern features, unlike Go which keeps things simple by sticking to familiar features dating back to the 70's), but does require more time to learn.
Rust is also an absolute pleasure to write. It feels like a functional Ruby (minus the bad parts) with types. The RAII is natural; writing C++ feels cludgy by comparison.
Cargo is absolutely brilliant, too. It's better than NPM.
I'm writing most of my new side projects in Rust now, FWIW.
Technical reasons alone don't clarify what's happening. Everyone's explaining basically why "Rust is awesome", which is not what was asked.
It's popular because Rust fans are heavily promoting it on HN as the solution to certain classes of programming errors. 2016 is probably going to be the year of Rust on HN, generally there's a new language every year such as Ruby, Javascript, etc. This year Go is a strong competitor and Elixir was looking strong but it fizzled out. :-)
It's controversial because users of other languages (understandably) don't want to rewrite their project, or they don't want to learn an entire new ecosystem that does exactly what the previous thing did, except in a safer way. Finally, some of them have enough experience to recognise that a new tool is never perfect, and we just haven't had enough time to figure out what the pitfalls and cons of Rust are.
The facts that Rust is quite young, has no shipping hero project as a case study and there are basically no jobs available are adding fuel to the fire.
Programmers are suckers for a good programming language story and love thinking that there's a language that is truly a joy to program in and will fulfill all their wishes. PL topics are always under heavy discussion.
> The facts that Rust is quite young, has no shipping hero project as a case study and there are basically no jobs available are adding fuel to the fire.
That's a bit of a misrepresentation though. Rust code is shipping in Firefox, and more importantly, Servo will have an alpha release in June.
The close integration between Rust and Servo development, as well as the rigorous pre 1.0 process also set it apart from other languages of comparable age.
I want to see a large-scale system written in Rust or a security-critical piece of infrastructure where it's demonstrable that defects were reduced. Of course, no one is obliged to fulfill my wish, it's just a criteria that I use to evaluate the maturity of a tool and that I think is very reasonable.
I think a browser is a large-scale, security critical piece of infrastructure.
Whether servo will truly have fewer leaks and security issues we'll have to see. They are also aiming for mor parallelism and therefore faster rendering....
Hard to say. I've seen posts by jamwt (I assume someone that's working on the said Rust project) which are more confusing than enlightening.
In one they say that Dropbox is investing multiple millions in a key piece of infrastructure written in Rust. One month later they say that the project is written in go with some Rust and it will "stay that way".
There was also a HN thread, maybe that contains more clues.
The fact that it was Go+Rust has never changed. Dropbox had a huge piece of infra being written in Go, they rewrote a core part of it in Rust and got massive savings from that. I presume a lot of that infra isn't bottlenecked by perf, so it doesn't make sense to rewrite it.
> In one they say that Dropbox is investing multiple millions in a key piece of infrastructure written in Rust. One month later they say that the project is written in go with some Rust and it will "stay that way".
Their overall infrastructure is in Go and they're fine with that, they've (re)written specific components in Rust for memory savings (and CPU though less so), they have ~60kLOC of rust in production according to jamwt comments on reddit.
I find your comment surprising, since Rust has been discussed on HN for over five years. It's been a popular topic for the past several. I also don't see much controversy, or "fire". It's interesting, so people are talking about it.
You're right on the pros but dead wrong (intentionally?) on the cons.
The real reasons it's controversial are: (1) it requires what some people consider excessive amount of annotations for borrowed types, resulting in visual noise, (2) it has an overly restrictive memory safety model (e.g. it prevents concurrent modification, even when provably safe), (3) it claims to be (memory) "safe", but requires (see 2) the use of "unsafe" features for the most basic tasks, such as implementing various collections.
There is other criticism (e.g. allowing overflows), but I think the above is the most controversial.
> the use of "unsafe" features for the most basic tasks, such as implementing various collections.
That's pretty much the _only_ place you need when using unsafe, when implementing "basic collections". The point of Rust's unsafe is to be able to use it to design safe abstractions. This is not a con; this is the _point_ of unsafe.
(Also, one can argue that implementing a collection is not a basic task. It's taught very early on in courses as a way of teaching pointers, but collections aren't simple in general and especially when it comes to memory management. See also: http://cglab.ca/~abeinges/blah/too-many-lists/book/)
> it prevents concurrent modification, even when provably safe
Are you talking about non-lexical borrows here (that will be fixed soon), or about Rust's restrictions in threaded scenarios? You can alleviate some of the threaded restrictions via scoped threads. Most of the non-threaded ones can be fixed (without a performance penalty) using Cell (RefCell for non-Copy types, though there's a slight cost there).
I've been programming in Rust for a while now, and this doesn't turn out to be an issue in practice since the regular way of designing code avoids this pretty well. Programming in Rust as if it was C++ leads to these errors a lot more.
> This is not a con; this is the _point_ of unsafe.
It's not a con if you consider C; but it most definitely is a con for a language that advertises itself as having "guaranteed memory safety".
> Are you talking about non-lexical borrows here (that will be fixed soon), or about Rust's restrictions in threaded scenarios?
I'm talking about the fact that the programmer has to write the code/algorithms in such a way that Rust's very limited proof engine can handle them. This means using collections in a limited way - if the API doesn't support something, you can't do it (an example would be, splitting a vector and updating each element by a separate thread - IIRC that used to be a problem, maybe it has been fixed already?). This also means implementing concurrent algorithms using the (limited) API (Cells, Atomics, ...) - although that's probably the preferred way in C as well, I'm guessing even things like CAS can be abstracted away with zero cost while improving safety. I'm curious, though, how would I implement the following: I have multiple threads, and I only care about the last result (i.e. they can all write to the same memory location)?
I agree, though, that this is rarely a problem in practice, which is why Rust is an amazing language. The criticism/controversy is mostly coming from outsiders and/or people like me who disapprove of false advertising in principle.
> It's not a con if you consider C; but it most definitely is a con for a language that advertises itself as having "guaranteed memory safety".
I explained in the other comment why this isn't really an issue. Note that most "basic datastructures" exist in the stdlib already, so you don't have to use unsafe code to do this in practice. 100% guaranteed memory safety by your definition is an unattainable goal. In safe rust code you do indeed have guaranteed memory safety, and you're able to extend this by writing unsafe rust code with which you supply your own guarantees of safety.
> splitting a vector and updating each element by a separate thread - IIRC that used to be a problem, maybe it has been fixed already?
This is scoped threads and is possible. Crossbeam and scoped_threadpool both have solutions for this.
> This also means implementing concurrent algorithms using the (limited) API
That's what you should be doing for thread safety. Like you mentioned, that's what you do in C too. Rust does indeed have CAS atomics.
> I have multiple threads, and I only care about the last result (i.e. they can all write to the same memory location)
This sounds like a use case for monotonic (relaxed) atomic ordering? Not sure.
Note that "the last result" itself doesn't mean anything, due to the way the cache works. There's no global clock (hooray for distributed systems!). The fact that Rust makes you think about this is a good thing. You can stick a Lamport clock in there, but that involves using more atomics and won't be as fast as relaxed ordering.
Non-atomic writes to a single memory location, from what may well be separate cores etc, happen essentially at random due to out-of-order execution. What are the downsides to using the value from the last thread ~spawned~, and calling it a day?
> (3) it claims to be (memory) "safe", but requires (see 2) the use of "unsafe" features for the most basic tasks, such as implementing various collections.
I think this is an interesting one - implementing collections is (IME) the main thing one needs to use unsafe for in rust. Outside of that I barely find myself needing it at all. Unfortunately, collections are also a go-to intro project for a lot of systems programmers, so I can see how it would be easy to get a bad first impression.
The collections criticism is bizarre. How often do you actually have to use "unsafe" in practice? Collections is one example, but it seems to be quite atypical, and not something you'd typically implement yourself.
The argument has always been that it reduces the amount of code you have to check for safety manually. Most of it will be checked by the compiler. Have you run into the problem that most of your code is unsafe?
I've only started dabbling in Rust for some projects, but I've not had to write a single line of unsafe yet, and don't see where that would change.
> The argument has always been that it reduces the amount of code you have to check for safety manually.
No, Rust's argument is that it eliminates memory unsafety. It even says so on it's front page: "guaranteed memory safety".
Except that it's not guaranteed. It relies on you trusting code that (supposedly) expert programmers have written, and which has been reviewed hundreds of times. Which is no better than C.
I agree that this criticism is mostly a theoretical one; Rust is still a huge improvement (in safety, and probably other areas) over C. But some people (e.g. myself) oppose lies in principle, no matter how well-intentioned they are.
> It relies on you trusting code that (supposedly) expert programmers have written, and which has been reviewed hundreds of times. Which is no better than C.
This is a vast oversimplification.
It relies on you trusting tiny bits of unsafe code which have been carefully reviewed. This is opposed to entire C codebases which need reviewing. It's much, much easier to audit 10 lines of unsafe code than it is to audit an entire application.
You will never be able to get both 100% guaranteed memory safety and low level control/performance. Opposing it based on principle means that you'll never move on to anything better because a perfect solution doesn't exist.
For what it's worth, you can write quite a lot in 100% safe code, such that you have to trust only the standard library. Which is the case for any programming language.
So, the argument on the front page is true. It does guarantee memory safety in safe Rust code, as much as possible. It gives you the tools to extend your abstractions with unsafe Rust code, which you need to supply your own guarantees with.
Since tomp objects to trusting code, then I agree with
Manishearth: if you're never willing to trust code, then you can never have guarantees. Even the proof engine is trusted code.
Well, you have to trust something... Silicone manufacturer, CPU designer, the OS, the compiler...
But the big difference is, if I can do proofs and the compiler breaks my trust, that's a bug in the compiler. On the orher hand, Rust disallows complex proofs entirely, so I have to use unsafe code and trust random humans!
This. There's no reason the smaller bits of `unsafe` code couldn't be verified with a proof system (ala COQ). Very few systems can be completely `true` in a mathematical sense, including math itself. IMHO, this is a self-defeating argument. E.g. a form of: "math isn't provable so math sucks!".
In the long run, this viewpoint would prevent moving to a system with 5% unsafe/95% safe which could be much more readily proofed using COQ since only a handful of unsafe code needs to be analyzed – I'm assuming proofing code has exponential complexity growth. So basically give Rust 10 years and I bet someone could build a verified stdlib & compiler for Rust!
Fantastic! Rust keeps getting better... Didn't realize Max Planck institute had a software division. Are you familiar with the state of their progress? I've been wondering when/how people might start applying monads to analyze shared imperative state. It appears to be somewhat of the approach in `Iris: Monoids and Invariants as an Orthogonal Basis for Concurrent Reasoning` paper listed. Going to have to keep an eye on these folks.
Even if the language pulled some magic trick where it could verify collection implementations without unsafe, all you'd have done is move the unsafety into the type checker and compiler backend.
The only difference is that Rust 1) moved some of that into the stdlib and 2) lets third parties extend it to provide more guarantees.
Given this, I think you're drawing a rather arbitrary line- Rust (the compiler + stdlib, optionally + other libraries wrapping unsafe) does guarantee memory safety, full stop. Whether the guarantees are provided by the stdlib or compiler is irrelevant.
It does guarantee memory safety... until you need to write your own collection. Then it explicitly endorses unsafe code (as opposed to offering a more powerful proof engine). I'm find with trusting something (compiler, proof engine, stdlib), but the issue is that safe Rust is barely a complete language!
This is a strange argument. The `unsafe` keyword only lets you do the following: dereference unsafe pointers, call unsafe functions (which usually either dereference unsafe pointers themselves, or are just wrappers over C functions, which are obviously unprovable), and mutate static data. Saying that safe Rust is "barely complete" is just as good as saying that Python is "barely complete" due to the fact that it doesn't allow you to read arbitrary locations in memory.
Relatively recent, trying for a fairly rarefied "niche" (that of C and C++) which many believe has issues (C++) and for which improvements have been in short supply, brings some innovations and safety to said niche (mostly decades-old FP stuff, but affine types are a bit more original, not exactly novel but not in common use either)
Hopefully this is relevant but one of the things I had difficulty with in Rust is closures (or more specifically the lifetime and pointers of closures) and wiring up a system or library that needs lots of callback things.
See I'm used to Java where anonymous classes are closures (I really like that about Java/Scala/Groovy. One of the very few things I don't like about C# is that does not have anonymous classes).
So the problem is when I want a callbacky thing that has state it gets fairly complicated in Rust because you have lots of options that have limitations. For example there is bare functions or raw functions which basically stateless, there is traits but you'll need to use boxes and or cells, and then there is Rust closures which I had some various issues with because of bugs (they are probably fixed now).
I only bring this up because instead of so many tutorials on simple static programming stuff I would like to see how one might wire up a system with many callback components (ie manual dependency injection). I suppose I should look at how its done in Iron.
ie if your going to show something analogous to a Java/C# developer show them some wiring up of a complicated system that has interfaces and the components can be interchanged (perhaps that is just something you don't do in Rust but I'm curious how it would be done).
> anonymous classes are closures (I really like that about Java/Scala/Groovy
Apache Groovy has diverged from Java since Java 8 came out, and there's also other JVM languages such as Kotlin. You only gave JVM languages as examples of the good, and all your examples of the bad were those on other platforms.
Groovy still has anonymous classes. And yes there are other languages that have anonymous classes I just didn't feel like enumerating them especially the OP article was about C#.
Let me just say this so there isn't any assumption that I was bashing C#... C# is a far far better language than Java with the only exception for me being it doesn't have anonymous classes.
Does anybody know how good the Rust integration with Windows is? The language looks very good but can you interop with Win32, COM and .NET? Is there a debugger? Without those its usability is a little limited.
Spec# has annotations and methods for object ownership. You can see an example of it in the "The Spec# Programming System: An Overview" slideshow (slide 39 onwards).
Also:
> C# has two families of data structures that dermine allocation behaviour: structs and classes. If a type is a struct then values of that type will be allocated on the stack:
Here's a primer on how the Microsoft CLR handles it, because it's more subtle than what's stated above. The end effect is that value types behave like values, reference types behave like references. Where they're stored is a function of where they need to be stored, not their type.
https://blogs.msdn.microsoft.com/ericlippert/2010/09/30/the-...