Rust updates
David Piepgrass
qwertie256 at gmail.com
Wed Jul 11 11:00:03 PDT 2012
> Rust has type classes from Haskell (with some simplifications
> for higher kinds), uniqueness typing, and typestates.
As nice as kinds, typestates, typeclasses and several pointer
types may be, I was in the Rust mailing list and felt unable to
participate because they kept using terminology that only PhD in
type systems understand. And googling for "kind" doesn't tell me
a darn thing ;)
That's why have gravitated to D, it's so much more familiar
(sometimes too much so, e.g. I still need to 'break' in 'switch'?
how many meanings for 'static'?) as well as very powerful. I
would still like to learn about the mumbo-jumbo, though, and I
know how nice pattern-matching can be from one Haskell-based
course in university :)
> This seems a bit overkill to me:
> This is very strict, maybe too much strict:
Agreed about the int suffixes, but I wonder what Marco meant
about "mass-casts" in D.
> The safe pointer types are @T for shared, reference-counted
> boxes, and ~T, for uniquely-owned pointers.
I wonder how well these could be simulated in D. It seems to me
Rust is carefully designed for performance, or at least real-time
performance by avoiding garbage collection in favor of safely
tracking ownership. That's good, but only now are they developing
things like OOP support that I take for granted.
> ++ and -- are missing
Rust, like Go, seems very focused on making a "simple" language.
Another reason that I prefer D.
> the logical bitwise operators have higher precedence. In C, x &
> 2 > 0 comes out as x & (2 > 0), in Rust, it means (x & 2) > 0,
> which is more likely to be what you expect (unless you are a C
> veteran).
Oh, I can't tell you what a pet peeve PITA the C precedence is.
Ugh! I know it's against D philosophy to change the precedence
w.r.t. C, but how about a compromise: give a warning or error for
"x&2 > 0", with error message: "add parenthesis around x&2 to
clarify your intention."
> Enums are datatypes that have several different
> representations. For example, the type shown earlier:
>
> enum shape {
> circle(point, float),
> rectangle(point, point)
> }
> fn angle(vec: (float, float)) -> float {
> alt vec {
> (0f, y) if y < 0f { 1.5 * float::consts::pi }
> (0f, y) { 0.5 * float::consts::pi }
> (x, y) { float::atan(y / x) }
> }
> }
> alt mypoint {
> {x: 0f, y: y_name} { /* Provide sub-patterns for fields */ }
> {x, y} { /* Simply bind the fields */ }
> }
> let (a, b) = get_tuple_of_two_ints();
Records, tuples, and destructuring go so well together. I would
love to have this.
I am particularly a fan of structural typing. I don't know if
Rust uses it but Opa and other functional languages often do. You
see, there's a problem that pops up in .NET all the time, and
probably the same problem exists in D.
Any time two libraries want to use the same concept, but the
concept is not in the standard library, they need to define it.
For instance if there is no "Point" type in the standard library,
but two unrelated libraries need points, they will both define
their own (amazingly, Points are poorly thought out in .NET and
tightly bound to GUI libraries, so people define their own in
some cases):
// JoesLibrary
struct Point!T { T x, y; /* followed by some manipulation
functions */ }
// FunkyLibrary
struct Point!T { T x, y; /* followed by other manipulation
functions */ }
Sadly, the two point types are not compatible with each other. A
client that wants to use both libraries now has an
interoperability problem when he wants to pass data between the.
Even a client that uses only one of the library, let's call it
"JoesLibrary" has to import Point from "JoesLibrary", even if its
functionality is not quite what the client wants. It would be
much nicer if the client could define his own Point struct that
seamlessly interoperates with Joes'. In D this is currently
impractical, but I would enjoy designing a way to make it work
(before you point out that "what if x and y are in a different
order in the two structs" and "it could be T X,Y in one and T x,y
in the other", yes, I know, It's on my list of problems to
cleverly solve)
A similar problem exists with interfaces, where two unrelated
libraries expose two similar classes with some common functions,
but you can't cast them to a common type in D. This is a solved
problem in Go (http://www.airs.com/blog/archives/277) and it's
actually pretty easy for a compiler to magically cast a class to
an interface that the class did not declare--if the underlying
language is designed for that, anyway.
In fact, in .NET at least, the same problem exists even if the
libraries DO know about each other and are even written by the
same person and use identical interfaces. The problem is, if I
write two libraries A and B, and I want them to be interoperable,
then I need to factor out the common structs and interfaces to a
microscopic third library, I. But from the client's perspective,
if a client only knows about A or B, he will think it's stupid
that I require him to use and deploy two DLLs/so's (A and I, or B
and I) instead of one. In D I guess it's not the same, though.
> Stack-allocated closures:
>
> There are several forms of closure, each with its own role. The
> most common, called a stack closure, has type fn& and can
> directly access local variables in the enclosing scope.
>
> let mut max = 0;
> [1, 2, 3].map(|x| if x > max { max = x });
I seem to recall that D has the ability to optimize closures to
make them stack-based when calling generic methods, or if the
called method happens to be inlined, am I right? However, Rust's
approach seems more powerful:
- Since it uses explicit notation, it could work across library
and DLL boundaries. The called function can promise it will not
save a reference to the closure, so the closure can always be
stack-based.
- I think Rust developed a "do" syntax that allows "break" and
"continue" (cont?) so that closures can be used in loops, so I
guess you could write your own while-loop for example. The syntax
is something like
do While(|| x > 0) { ... if (...) cont; ... }
if I'm not mistaken and going senile.
> Unique closures, written fn~ in analogy to the ~ pointer type
> (see next section), hold on to things that can safely be sent
> between processes. They copy the values they close over, much
> like boxed closures, but they also 'own' them—meaning no
> other code can access them. Unique closures are used in
> concurrent code, particularly for spawning tasks.
Hmm, hmm. Is there anything analagous in D?
> By default, a module exports everything that it defines. This
> can be restricted with export directives at the top of the
> module or file.
>
> mod enc {
> export encrypt, decrypt;
> const super_secret_number: int = 10;
> fn encrypt(n: int) -> int { n + super_secret_number }
> fn decrypt(n: int) -> int { n - super_secret_number }
> }
I much prefer D's approach. It's much more convenient for the
code writer, and Rust's approach is better or worse for someone
reading the code depending on whether one wants to know "what
functions are public?" or "is this function I'm looking at
public?"
> For some info on the typestate system, from the Rust manual:
> http://dl.rust-lang.org/doc/rust.html#typestate-system
Oh, do I ramble on or what? I'd love to talk about that, but
would you look at the time...
More information about the Digitalmars-d
mailing list