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