Rust for C++ Programmers Part 7: Data Types
featherweightmusings.blogspot.com
Rust for C++ Programmers Part 7: Data Types
1–10 of 39 posts
Re: Rust for C++ Programmers Part 7: Data Types
#2 struct IntPoint (int, int);
fn foo(x: IntPoint) {
let IntPoint(a, b) = x; // Note that we need the name of the tuple
// struct to destructure.Re: Rust for C++ Programmers Part 7: Data Types
#3I'm curious why this isn't automatically destructured: struct IntPoint (int, int); fn foo(x: IntPoint) { let IntPoint(a, b) = x; // Note that we need the name of the tuple // struct to destructure.
Hm.
Re: Rust for C++ Programmers Part 7: Data Types
#4I'm curious why this isn't automatically destructured: struct IntPoint (int, int); fn foo(x: IntPoint) { let IntPoint(a, b) = x; // Note that we need the name of the tuple // struct to destructure.
Re: Rust for C++ Programmers Part 7: Data Types
#5Re: Rust for C++ Programmers Part 7: Data Types
#6I'm curious why this isn't automatically destructured: struct IntPoint (int, int); fn foo(x: IntPoint) { let IntPoint(a, b) = x; // Note that we need the name of the tuple // struct to destructure.
struct Foo(int, int);
fn bar(Foo(a, b): Foo) {
println!("a: {}, b: {}", a, b);
}
fn main() {
let qux = Foo(1, 2);
bar(qux); // a: 1, b: 2
}Re: Rust for C++ Programmers Part 7: Data Types
#7I'm curious why this isn't automatically destructured: struct IntPoint (int, int); fn foo(x: IntPoint) { let IntPoint(a, b) = x; // Note that we need the name of the tuple // struct to destructure.
For some reason I have an instinctual reaction that you should have to specify the type, but on reflection I'm not sure why. It is completely redundant, and since the fields aren't named for the purposes of a destructuring assignment like this any 2-field tuple is essentially equivalent. Hm.
For example, say you have two functions, where each function takes a single tuple of two floating point numbers:
// Converts a Cartesian coordinate to a polar coordinate
fn to_polar(coord: (f64, f64)) -> (f64, f64) { ... }
// Calculates the area of a rectangle
fn area(rect: (f64, f64)) -> f64 { ... }
Now, if you have a tuple that represents a coordinate, perhaps you don't want to feed it to the `area` function. Likewise for feeding a rectangle to the `to_polar` function. But because tuples are just structural types, something like `area(to_polar((2.5, 3.7))` is completely legal.If you didn't want to allow this, or even if you just wanted to have greater control over all these anonymous tuples floating around, you'd use tuple structs to give them names:
struct CarteCoord(f64, f64);
struct PolarCoord(f64, f64);
struct Rectangle(f64, f64);
struct Area(f64); // bonus round!
fn to_polar(coord: CarteCoord) -> PolarCoord { ... }
fn area(rect: Rectangle) -> Area { ... }
Taking the above steps makes `area(to_polar(CarteCoord(2.5, 3.7)))` a compile-time error. It's all about how strict you want to be with your types.Re: Rust for C++ Programmers Part 7: Data Types
#8I would love to see an article like this for error handling in Rust. I was really interested in using conditions, but those were apparently backed out. Which leaves error handling through return values and macros. This seems like a step back from Exceptions to me. I want to be convinced otherwise, but I'm struggling to see how this is better than other mechanisms.
fn can_fail(arg: bool) -> Result {
if arg {
Ok(())
} else {
Err(StrBuf::from_str("Oops! Something went wrong."))
}
}
Here, there's no value when the function succeeds `()`. When it fails (I don't mean fail as in a `fail!()` or a panic or anything), you get a string back.You can pattern match the return value:
match can_fail(true) {
Ok(_) => {},
Err(err) => {}
}
This can get quite cumbersome, however. That's why there's a `try!` macro that adds composability. The idea is that if you have a function returning a `Result`, wherever that function is being called could also return a `Result`. fn higher_up() -> Result {
try!(can_fail(true));
}
`try!` is simply: match $e { Ok(e) => e, Err(e) => return Err(e) }
This allows error to propagate up the chain.When you're working in big-ish projects, it'd be best to have something better than a simple `StrBuf` for an error. You'd probably want a struct:
pub struct LibError {
message: StrBuf,
error: Error
}
pub enum Error {
One,
Two,
Three
}
Where `Lib` is the library/project name.You can then create a new result type based on your new error type:
type LibResult = Result;
Then you can use `LibResult` everywhere in your app.You can view this example done in a few of my own libraries (https://github.com/TheHydroImpulse/gossip.rs/blob/master/src...) and cargo (https://github.com/carlhuda/cargo/blob/master/src/cargo/util...).
That's a simple overview of it. Error handling is super simple, not verbose (thanks to try!) and in your control. Because of Rust's type system, things like `Result` is available and are so much better than simple return values (like integers: -1 vs 0 uhhh)
Re: Rust for C++ Programmers Part 7: Data Types
#9I would love to see an article like this for error handling in Rust. I was really interested in using conditions, but those were apparently backed out. Which leaves error handling through return values and macros. This seems like a step back from Exceptions to me. I want to be convinced otherwise, but I'm struggling to see how this is better than other mechanisms.
Re: Rust for C++ Programmers Part 7: Data Types
#10I would love to see an article like this for error handling in Rust. I was really interested in using conditions, but those were apparently backed out. Which leaves error handling through return values and macros. This seems like a step back from Exceptions to me. I want to be convinced otherwise, but I'm struggling to see how this is better than other mechanisms.
Error handling in Rust is actually pretty awesome. There's a standard `Result ` type. One can write a potentially fail-able function like: fn can_fail(arg: bool) -> Result { if arg { Ok(()) } else { Err(StrBuf::from_str("Oops! Something went wrong.")) } } Here, there's no value when the function succeeds `()`. When it fails (I don't mean fail as in a `fail!()` or a panic or anything), you get a string back. You can p…
enum FooError {
XWasFalse,
XWasUnknown
}
enum BarError {
VectorTooLarge,
FooErr(FooError)
}
fn foo(x: Option) -> Result {
match x {
Some(true) => Ok(42),
Some(false) => Err(XWasFalse),
_ => Err(XWasUnknown)
}
}
fn bar() -> Result, BarError> {
foo(None).or_else(|e| {
// We can recover from an XWasUnknown error returned by
// Foo, but not from a XWasFalse, so we return the error wrapped
// in bar's error type.
match e {
XWasFalse => Err(FooErr(e)),
XWasUnknown => Ok(99)
}
}).and_then(|n| {
if n = Vec::with_capacity(n);
Ok(vec)
} else {
Err(VectorTooLarge)
}
}).map(|vec| {
vec.iter().map(|_| { 42 }).collect()
})
}
edit: added a better example