bearophile
| I've just read this tutorial about Rust and I've extracted some interesting bits: http://doc.rust-lang.org/doc/tutorial.html
My comments are in inside [...].
--------------------
From 3.9:
The compiler defines a few built-in syntax extensions. The most useful one is #fmt, a printf-style text formatting macro that is expanded at compile time.
std::io::println(#fmt("%s is %d", "the answer", 42));
#fmt supports most of the directives that printf supports, but will give you a compile-time error when the types of the directives don't match the types of the arguments.
[In D I hope we'll something like: cwriteln!"%s is %d"("the answer", 42) that tests the formatting string at compile-time, avoiding run-time errors.]
--------------------
From 4.2:
A powerful application of pattern matching is destructuring, where you use the matching to get at the contents of data types. Remember that (float, float) is a tuple of two floats:
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) }
}
}
[For D see http://d.puremagic.com/issues/show_bug.cgi?id=596 but it's simpler, no if guards]
--------------------
4.3 Destructuring let
To a limited extent, it is possible to use destructuring patterns when declaring a variable with let. For example, you can say this to extract the fields from a tuple:
let (a, b) = get_tuple_of_two_ints();
This will introduce two new variables, a and b, bound to the content of the tuple.
[There is a pull request for this in D.]
--------------------
5.1.3 Unique closures
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 mostly exist for spawning new tasks.
--------------------
From 5.3:
To run such an iteration, you could do this:
for_rev([1, 2, 3], {|n| log(error, n); });
Making use of the shorthand where a final closure argument can be moved outside of the parentheses permits the following, which looks quite like a normal loop:
for_rev([1, 2, 3]) {|n|
log(error, n);
}
[A similar simple syntax sugar was proposed for D too.]
--------------------
From 6.6:
Rust supports several types of pointers. The simplest is the unsafe pointer, written *TYPE, which is a completely unchecked pointer type only used in unsafe code (and thus, in typical Rust code, very rarely). The safe pointer types are @TYPE for shared, reference-counted boxes, and ~TYPE, for uniquely-owned pointers.
All pointer types can be dereferenced with the * unary operator.
--------------------
From 7.4:
Then there is the by-copy style, written +. This indicates that the function wants to take ownership of the argument value. If the caller does not use the argument after the call, it will be 'given' to the callee. Otherwise a copy will be made. This mode is mostly used for functions that construct data structures. The argument will end up being owned by the data structure, so if that can be done without a copy, that's a win.
type person = {name: str, address: str};
fn make_person(+name: str, +address: str) -> person {
ret {name: name, address: address};
}
--------------------
9.7 Exporting
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 }
}
This defines a rock-solid encryption algorithm. Code outside of the module can refer to the enc::encrypt and enc::decrypt identifiers just fine, but it does not have access to enc::super_secret_number.
[In Haskell and Python there is something similar. In D you mark with "private" the global names of a module you don't want to export. But I think listing them all at the top of the module allows you to control them better.]
--------------------
13 Testing
The Rust language has a facility for testing built into the language. Tests can be interspersed with other code, and annotated with the #[test] attribute.
use std;
fn twice(x: int) -> int { x + x }
#[test]
fn test_twice() {
let i = -100;
while i < 100 {
assert twice(i) == 2 * i;
i += 1;
}
}
When you compile the program normally, the test_twice function will not be included. To compile and run such tests, compile with the --test flag, and then run the result:
> rustc --test twice.rs
> ./twice
running 1 tests
test test_twice ... ok
result: ok. 1 passed; 0 failed; 0 ignored
Or, if we change the file to fail, for example by replacing x + x with x + 1:
running 1 tests
test test_twice ... FAILED
failures:
test_twice
result: FAILED. 0 passed; 1 failed; 0 ignored
[D unittesting needs some improvement here.]
--------------------
Bye,
bearophile
|