It is one of those cases where you can actually leak memory with a GC. But at least Java gives (gave?) you a choice: to create a new string a use the current one, in .NET substring always returns a new string.
In what sense this introduction is alternative? It's just like every other intro that explains basic and easily understood things about the borrow checker, but doesn't go into details on how you actually live and work with the damned thing. The second thing you do after learning basics is trying to implement some simple data structures, like lists, or even (horror!) double-linked lists. In Rust this is the moment where you start hitting roadblocks, and then you read somewhere that implementing "basic" data structures in Rust isn't a basic exercise at all, and that is kind of discouraging.
This seems like an unfortunate pathological case caused by the abstraction level Rust sits at. The kinds of tree- and graph-like data structures that garbage collected languages can represent in safe code (at the cost of performance) don't work as well in Rust; you can still implement them with various mechanisms (std::swap is very important for trees; Rc and Weak; replacing pointers with indices into a global Vec; etc.), but due to a desire to get optimal low-level performance, core data structures often end up using unsafe blocks in their implementations. The thing is, though, implementing data structures is something of a worst case: in most other code you can use the data structures to provide the pointer relationships you need, but when implementing them you have to do that yourself. Most other code is more naturally amenable to working with the borrow checker - which is not to say it's not difficult.
This isn't unique to Rust, in fact, as modern C++ presents the same dilemma. Here [1] is a Stack Overflow question where someone asks how to make a doubly-linked list out of smart pointers, and all the answers either involve shared_ptr or C pointers. Same pedagogical issue: you want to deemphasize the less-safe pointers (in Rust, the ones behind an "unsafe" gate; in C++, the raw C pointers) in favor of the safer abstractions, but you hit a wall when it comes to doubly-linked lists.
C++ has it easier in practical terms, I guess—few projects actually use smart pointers as pervasively as modern C++ guidelines stipulate (and they're hard-wired into the language in a couple places like operator new and this), so C pointers usually have to be taught pretty early anyway. In Rust, by contrast, unsafe pointers are considered evil things you touch only when you know you have to. But the problem is still there: it feels like something that comes with the territory of manual memory management in general.
You need to use weak pointers for backpointers in Rust, because there's no GC. If you create a cycle with reference counted objects, objects are never released and the program will leak memory.
There's little benefit to doing so. Because weak_ptr points to a shared_ptr, you are still paying the overhead of the reference counting either way. You might as well just use shared_ptr and have a destructor that unlinks every element from the list.
weak_ptr is basically for managing leaks, not for avoiding reference count overhead. Leaks aren't the problem in a doubly-linked list, however.
I guess this would solve the problem but it would also be slow because traversing the list via weak_ptr requires a conversion from weak_ptr to shared_ptr for every item.
There isn't any choice in RC systems in presence of cycles, either weak pointers or a presence of cycle collector.
Anything else is unsafe, specially if the pointer is allowed to escape the RC object that contains it.
And this is one of the reasons why usually optimized GCs outperform RC solutions.
Unless approaches like affine types or counting elision are used, but those fail down in the presence of cycles, forcing the developers to use other approaches to represent cycles.
Well, I wrote our existing introduction, this is a different version, that takes a fundamentally different approach: small example, high level of detail, rather than larger examples at a higher level of detail.
Your point about it being an introduction stands, but that's why it's an alternate _introduction_.
There is no need to drag shale gas into this. Ukraine is a big country, even if very poor (for now). The economic part of the association agreement was especially damaging to the economic relationship between Russia and Ukraine. The EU played zero-sum game here, hoping (and not without reason) that the government will be forced to sign it anyway. It's just the whole thing escalated well beyond anyone would expect. Apparently, it was a mistake to impose such dividing agreements on an already deeply divided nation, the lesson the EU learned too late.
I think the poster means that c# has many functional features and F#, while being functional language at heart has lots of OO features integrated in it.
PS And the linked question only describes the limitations of Visual Studio where you can have only one language in a given project.
> I think the poster means that c# has many functional features
Yeah just like C++ or Java now have "functional features". That doesn't make them functional languages, they don't promote functional programming to the extent that Scala does.
Aside from that, their type systems are much too unexpressive to allow for the style functional programmers are used to, not to mention they promote mutability.
In the worst case scenario the time complexity of a hash table equals that of its bucket, which doesn't have to be a list. So the only advantage of the map over the unordered_map seems to be the ease of use, it only needs a comparer.
Only three rust benchmarks use unsafe code, in one of them it's virtually mandatory, and in another it's just one line. On a side note, rust compiler enforces naming conventions, which is kind of cute.
The reverse complement program certainly doesn't need unsafe code, but the safe rust version[1] takes 3x the execution time of the unsafe rust version[2]. By comparison the fastest Go version[3] is fairly idiomatic Go without unsafe memory management and takes just 2x the time of the unsafe rust version.
Also, which benchmark did you have in mind when you said unsafe code is mandatory?
These are REALLY BAD examples, the safe rust version isn't parallelized but the two others are. The unsafe version is just translated C as people already stated. A safe version these days (heading towards 1.0) shouldn't be far of the unsafe versions.
That specific safe version, which, IIRC, was a basic translation of the fastest C version. I'd guess that there's other safe variations that are more idiomatic and much faster.
May be a faster safe version should be contributed. The current state is that unsafe rust takes half as much time as a safe go program in this benchmark. That is not very interesting because the benefits offered by rust are being given up to perform better, for a problem that doesn't really need unsafe memory access.
Same is the case with spectral norm. It is unsafe rust beating safe go there too.