And thats why I expected the most frequent case of borrowing would be implicit so I don't have to annotate my code until I need it, but it isn't because of some mostly subjective choice to be more explicit by default. I thought the slogan was "zero cost" abstractions.
Edit: especially I shouldn't be forced to annotate pure functions. Obviously they can safely borrow.
I've read that fully optimized immutable functional programming can only get to within a log factor performance of imperative mutable programming. And as you allude, sometimes it is much more difficult to structure the same algorithm in an immutable way.
I think it is often more useful to think in higher-level terms. Depending on what the use case and design is, there may be very elegant ways to provide the necessary guarantees rather than falling back to a finely grained bottom-up approach to a memory ownership model. The fine grained approach intertwines a lot of minute invariant declarations and inferrants. Sometimes there may be a higher level design solution. That is why I am saying that Rust's memory ownership model everywhere is not likely to be optimum. It will hopefully have some very well matched use cases.
Saying that "explicit is better than implicit" is a shorthand for a more nuanced situation. The borrowck is a fully integrated part of Rust's type system, & is an operator which transforms a type T in a scope 'a to a type &'a T. A language could perform this operation implicitly in certain expression positions, but this results in a real hairy mess in my opinion. I definitely prefer telling the compiler when I want to perform this operation to the compiler applying some arcane heuristic to perform it for me.
My impression is that proper whole program lifetime inference is undecidable in a turing complete language, but I don't know if that's ever been proved.
Speaking more generally, you seem very interested in the big picture language design questions that Rust has encountered and grappled with. These are interesting and exciting questions! I would just encourage you to learn and use Rust more thoroughly before coming to conclusions about the effectiveness or viability of its approach in different contexts.
Afaics, the linked discussion seemed to indicate there wasn't a potential hairy mess, but I suggest our detailed discussion if any (and I'm not pushing for it as I think the decision has already been made) should go in that linked thread, not here.
Without any implicit conversions at all, languages become a hairy mess of noise. So to argue against one set of implicit conversions (while retaining others) by implicitly claiming you don't want the compiler to do any implicit conversions is really an arbitrary thus vacuous reason. Rather we need to either show ambiguities it creates and discuss the tradeoffs of the (what is actually mostly a subjective) choice.
I mean, I am not interested in Rust because I am manly..
Python is excellent for exploratory programming - I have used it for that myself.
Lots of highly talented people are using that language.
However, my 'calling' is graphics programming, so I am a C++ programmer (and not a very good one) - Rust seems to be a Godsend: the best of all worlds.
I need full control - to the metal - and if I can get that and have high level language features at the same time: win!
Also, I need smooth interop with C, and Rust have that.
And it does not require a massive runtime - Python is not really easily deployable..
I will probably still use Python for certain things, although - coming from a C and C++ background - I would probably try and use Rust more in those Python'esque situations..
Given the programmer of a Haskell clone that compiles to Javascript—which Haskell donated two of its Google Summer of Code slots to—documents that the Num(ber) class is constructed from the typeclasses (aka traits) Semiring, Ring, and Moduloring, I maintain that the target demographic for Haskell are the more mathematically minded.
One can produce programs without knowing how the libraries for a language are designed, but to be an expert one typically needs to understand the "nuts and bolts" of the language and the libraries.
Edit: for example, understanding one way to do coroutines in Haskell.
Edit#2: An explanation of the Applicative functor for Haskell, goes into how side-effects aren't allowed for pure functions, yet we can model side-effects in the higher-level abstraction of the Applicative functor. This has the point of being a counter-example for donallen and also pointing out that it is possible to model mutability in immutable constructions by lifting the abstraction (and there are no memory leaks within the pure context, although we can create memory leaks when we model mutability yet our abstraction isolates and types the mutability so other potential means of automated static analysis could be envisioned).
In my experience, even with a garbage collector, developers aren't completely free from memory management. Garbage collectors definitely help make sure memory accessed is always valid. However, I can not tell you how many times I have had to debug memory leaks because other developers don't consider what holds onto a reference and how long that reference will live. I can honestly say I have become a better Android/Java developer because Rust has been forcing me to think about these things and that even with a GC they can not be taken for granted.
I liked your post because it is a good counter-argument. I want to point out though that I had pointed to higher-level paradigm shifts to attack the problem of memory leaks (which in some localities entirely eliminate thinking about memory management in some cases, e.g. pure functions), other than Rust's fine-grained (low-level) approach. So I'll continue to argue that Rust's approach is not going to be employed everywhere.
I've taken a hard look at Rust and I don't think it's appropriate for
situations where ultimate performance isn't necessary. Systems that provide
garbage collection are much easier to use. In Rust, you are doing memory
management manually, as you do in C, but Rust insures that you do it
correctly, which C does not. Garbage collection removes memory management
from the programmer's set of responsibilities.
In my experience, even with a garbage collector, developers aren't
completely free from memory management. Garbage collectors definitely help
make sure memory accessed is always valid. However, I can not tell you how
many times I have had to debug memory leaks because other developers don't
consider what holds onto a reference and how long that reference will live.
I can honestly say I have become a better Android/Java developer because
Rust has been forcing me to think about these things and that even with a
GC they can not be taken for granted.
I was talking about the lesser load garbage-collected languages place on
the programmer, compared with languages like C and Rust, where the
allocation and deallocation of memory is the programmer's responsibility. I
did not intend to make the claim, though I can understand why you thought I
did (because of the way I wrote the last sentence above; what I meant by
the phrase "memory management" was explicit allocation and deallocation)
that there is no way to write code that uses far too much memory in systems
equipped with GCs. I have written a lot of Lisp and Scheme over a period of
more than 40 years and have had to pay attention to exactly the issue you
raise (especially 40 years ago, when memory was a much more scarce resource
than it is today) -- retaining references unnecessarily; this is why many
Lisp implementations provide "weak" references. Unnecessary consing is a
similar consideration, as is to take advantage of tail-recursion when
possible.
[Moderator note: Fixed some posts with broken Markdown formatting, and removed two comments discussing the formatting issue. Please feel free to flag posts (choose the "Something Else" option) with mangled formatting, or PM the author. Users replying by email, please make sure quoted text is followed by a blank line.]
Eager coercions are a very hackish feature. They interact terribly with inference, causing order-dependency and other craziness. If you add too much of them, type inference becomes so loosely-coupled it tears itself apart (see functions-destroy-closure-inference).
Rust's original approach was to use Rc (then @) everywhere so that nobody had to think about memory management. It's just that we found Rust's ownership/borrowing semantics to be sufficiently nice we started using them everywhere, through @ still exists.
I think that you're arguing with a strawman here. I don't think anyone thinks that Rust is a be-all, end-all language to replace all use cases.
I think that all that is claimed are the following:
GC is also not a be-all, end-all answer to memory and resource management; see the fact that C and C++ still exist when there are many GC'd languages to choose from
Purely functional programming is also not a be-all, end-all answer to problems of memory management, resource management, and parallelism
It is possible to provide performance characteristics, including a mental model to judge and tweak performance characteristics, similar to C or C++, without exposure to the very unsafe semantics that they have
It is possible to provide higher level constructs and nice tools that are commonly used in functional programming in such a language conveniently, efficiently, and safely
The tools used to provide such type and memory safety are also useful for forming better organization of code than you might have in either C, C++, or higher level GC'd languages; the notion of who owns an object, who has a unique borrowed reference to an object, and who has a shared immutable reference to an object is something that is not made explicit in most other languages, but can be very useful for keeping track of your semantics and rules about who is allowed to do what.
This combination of high-level features, some good thought on API design, and tools that help you structure your code a little better, can also mean that Rust is suitable to use beyond the the use cases that you might expect if you just focus on point 3. Point 3 is what most people think of as the reason for using Rust, the thing that makes it stand out, but the supply of reasonable high level abstractions, tools to help you reason a little better about ownership and sharing, and then some thoughtful API and ecosystem design around it, help make it appropriate for other tasks outside that core niche that differentiates it the most.
I don't think anyone thinks that Rust's approach is going to be employed everywhere, just that you shouldn't think of it as a one-trick pony for low-level, performance sensitive stuff where you want safety, and that it's actually applicable to a much broader range of applications.
I am open-minded to your points and hope to see the use cases where Rust shines. Per my prior reply to arielb1, maybe iterators were not such a great idea, yet they are well matched to the low-control over resource conflicts, so I think that could possibly serve as an example of:
I don't know if a well designed GC functional programming language could displace the C++ lineage entirely. I am giving it some thought. And I don't claim to be omniscient. The community collectively knows more than any one of us.
I have sketched a proposal to make Rust the afaik first programming language in the world that can afaics completely solve Wadler's famous Expression Problem. This would I think perhaps elevate Rust to one of the best high-level programming languages.