I've been reading the blogs about rust and this closure for example made me wonder:
fn each<E>(t: &Tree<E>, f: &fn(&E) -> bool) {
if !f(&t.elem) {
return;
}
for t.children.each |child| { each(child, f); }
}
why couldn't it be:
each<E>(t: &Tree<E>, f: &(&E) -> bool) {
if !f(&t.elem) {
return;
}
for t.children.each |child| { each(child, f); }
}
Maybe i'm missing something on the class system that would prevent this.
It makes parsing more complicated for compilers, syntax-highlighters, shell scripts and humans (i.e. everyone).
For example, with fn, foo takes a function that has two int arguments and returns nothing, and bar takes a pointer to a tuple of 2 ints
fn foo(f: &fn(int, int)) {}
fn bar(t: &(int, int)) {}
Without fn, the arguments for both become &(int, int) and the compiler couldn't distinguish them. Sure one could come up with other rules so they are written differently, but these almost certainly have no advantages over just using fn.
Some of the fn's may seem extraneous, but this has a side benefit that rust code is extremely easy to navigate with 'grep'. To find the definition of function 'foo', you simply grep "fn\sfoo". To see the major definitions in a source file, just grep for "(fn|struct|trait|impl|enum|type)".
This is extremely helpful at this early stage when rust lacks IDE's, and probably does simplify the grammar in other ways.
Making the grammar less ambiguous than C++ is a major goal , it makes generic programming easier (you dont have to bring in so much definition into a compile unit, in a specific order, to correctly parse it), and should make future tooling easier. A feature to auto-insert 'fn's would be pretty straightforward compared to many of the issues current C++ IDE's have to deal with.
Related
I want to write a macro_rules based macro that will be used to wrap a series of type aliases and struct definitions. I can match on "items" with $e:item, but this will match both aliases and structs. I would like to treat the two separately (I need to add some #[derive(...)] just on the structs). Do I have to imitate their syntax directly by matching on something like type $name:ident = $type:ty; or is there a better way? This route seems annoying for structs because of regular vs tuple like. If I also wanted to distinguish functions that would be really painful because they have a lot of syntactical variation.
I believe for that problem somewhat simple cases can be solved with macro_rules!, but that probably would be limited (you can't lookahead) and super error-prone. I only cover an example for types, I hope that would be convincing enough to avoid macro_rules!. Consider this simple macro:
macro_rules! traverse_types {
($(type $tp:ident = $alias:ident;)*) => {
$(type $tp = $alias;)*
}
}
traverse_types!(
type T = i32;
type Y = Result<i32, u64>;
);
That works fine for the trivial aliases. At some point you probably also would like to handle generic type aliases (i.e. in the form type R<Y> = ...). Ok, your might still rewrite the macro to the recursive form (and that already a non-trivial task) to handle all of cases. Then you figure out that generics can be complex (type-bounds, lifetime parameters, where-clause, default types, etc):
type W<A: Send + Sync> = Option<A>;
type X<A: Iterator<Item = usize>> where A: 'static = Option<A>;
type Y<'a, X, Y: for<'t> Trait<'t>> = Result<&'a X, Y>;
type Z<A, B = u64> = Result<A, B>;
Probably all of these cases still can be handled with a barely readable macro_rules!. Nevertheless it would be really hard to understand (even to the person who wrote it) what's going on. Besides, it is hard to support new syntax (e.g. impl-trait alias type T = impl K), you may even need to have a complete rewrite of the macro. And I only cover the type aliases part, there's more to handle for the structs.
My point is: one better avoid macro_rules! for that (and similar) problem(-s), procedural macros is a way much a better tool for that. It easier to read (and thus extend) and handles new syntax for free (if syn and quote crates are maintained). For the type alias this can be done as simple as:
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream};
struct TypeAliases {
aliases: Vec<syn::ItemType>,
}
impl Parse for TypeAliases {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut aliases = vec![];
while !input.is_empty() {
aliases.push(input.parse()?);
}
Ok(Self { aliases })
}
}
#[proc_macro]
pub fn traverse_types(token: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(token as TypeAliases);
// do smth with input here
// You may remove this binding by implementing a `Deref` or `quote::ToTokens` for `TypeAliases`
let aliases = input.aliases;
let gen = quote::quote! {
#(#aliases)*
};
TokenStream::from(gen)
}
For the struct parsing code is the same using ItemStruct type and also you need a lookahead to determine wether it's an type-alias or a struct, there's very similar example at syn for that.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Since using unwrap may be problematic because it crashes in the error scenario, it may be considered as dangerous usage.
What if I am hundred percent sure that it will not crash, like in the following scenarios:
if option.is_some() {
let value = option.unwrap();
}
if result.is_ok() {
let result_value = result.unwrap();
}
Since we already checked the Result and Option there will be no crash with the unwrap() usage. However, we could have used match or if let. In my opinion, either match or if let usage is more elegant.
Let's focus on Result; I'll go back to Option at the end.
The purpose of Result is to signal a result which may succeed or fail with an error. As such, any use of it should fall into this category. Let's ignore the cases where a crate returns Result for impossible-to-fail operations.
By doing what you are doing (checking if result.is_ok() then extracting the value), you're effectively doing the same thing twice. The first time, you're inspecting the content of the Result, and the second, you're checking and extracting unsafely.
This could indeed have been done with a match or map, and both would have been more idiomatic than an if. Consider this case for a moment:
You have an object implementing the following trait:
use std::io::{Error, ErrorKind};
trait Worker {
fn hours_left(&self) -> Result<u8, Error>;
fn allocate_hours(&mut self, hours: u8) -> Result<u8, Error>;
}
We're going to assume hours_left() does exactly what it says on the tin. We'll also assume we have a mutable borrow of Worker. Let's implement allocate_hours().
In order to do so, we'll obviously need to check if our worker has extra hours left over to allocate. You could write it similar to yours:
fn allocate_hours(&mut self, hours: u8) {
let hours_left = self.hours_left();
if (hours_left.is_ok()) {
let remaining_hours = hours_left.unwrap();
if (remaining_hours < hours) {
return Err(Error::new(ErrorKind::NotFound, "Not enough hours left"));
}
// Do the actual operation and return
} else {
return hours_left;
}
}
However, this implementation is both clunky and inefficient. We can simplify this by avoiding unwrap and if statements altogether.
fn allocate_hours(&mut self, hours: u8) -> Result<u8, Error> {
self.hours_left()
.and_then(|hours_left| {
// We are certain that our worker is actually there to receive hours
// but we are not sure if he has enough hours. Check.
match hours_left {
x if x >= hours => Ok(x),
_ => Err(Error::new(ErrorKind::NotFound, "Not enough hours")),
}
})
.map(|hours_left| {
// At this point we are sure the worker has enough hours.
// Do the operations
})
}
We've killed multiple birds with one stone here. We've made our code more readable, easier to follow and we've removed a whole bunch of repeated operations. This is also beginning to look like Rust and less like PHP ;-)
Option is similar and supports the same operations. If you want to process the content of either Option or Result and branch accordingly, and you're using unwrap, there are so many pitfalls you'll inevitably fall into when you forget you unwrapped something.
There are genuine cases where your program should barf out. For those, consider expect(&str) as opposed to unwrap()
In many, many cases you can avoid unwrap and others by more elegant means. However, I think there are situations where it is the correct solution to unwrap.
For example, many methods in Iterator return an Option. Let us assume that you have a nonempty slice (known to be nonempty by invariants) and you want to obtain the maximum, you could do the following:
assert!(!slice.empty()); // known to be nonempty by invariants
do_stuff_with_maximum(slice.iter().max().unwrap());
There are probably several opinions regarding this, but I would argue that using unwrap in the above scenario is perfectly fine - in the presence of the preceeding assert!.
My guideline is: If the parameters I am dealing with are all coming from my own code, not interfacing with 3rd party code, possibly assert!ing invariants, I am fine with unwrap. As soon as I am the slightest bit unsure, I resort to if, match, map and others.
Note that there is also expect which is basically an "unwrap with a comment printed in the error case". However, I have found this to be not-really-ergonomic. Moreover, I found the backtraces a bit hard to read if unwrap fails. Thus, I currently use a macro verify! whose sole argument is an Option or Result and that checks that the value is unwrapable. It is implemented like this:
pub trait TVerifiableByVerifyMacro {
fn is_verify_true(&self) -> bool;
}
impl<T> TVerifiableByVerifyMacro for Option<T> {
fn is_verify_true(&self) -> bool {
self.is_some()
}
}
impl<TOk, TErr> TVerifiableByVerifyMacro for Result<TOk, TErr> {
fn is_verify_true(&self) -> bool {
self.is_ok()
}
}
macro_rules! verify {($e: expr) => {{
let e = $e;
assert!(e.is_verify_true(), "verify!({}): {:?}", stringify!($e), e)
e
}}}
Using this macro, the aforementioned example could be written as:
assert!(!slice.empty()); // known to be nonempty by invariants
do_stuff_with_maximum(verify!(slice.iter().max()).unwrap());
If I can't unwrap the value, I get an error message mentioning slice.iter().max(), so that I can search my codebase quickly for the place where the error occurs. (Which is - in my experience - faster than looking through the backtrace for the origin of the error.)
I like using partial application, because it permits (among other things) to split a complicated function call, that is more readable.
An example of partial application:
fn add(x: i32, y: i32) -> i32 {
x + y
}
fn main() {
let add7 = |x| add(7, x);
println!("{}", add7(35));
}
Is there overhead to this practice?
Here is the kind of thing I like to do (from a real code):
fn foo(n: u32, things: Vec<Things>) {
let create_new_multiplier = |thing| ThingMultiplier::new(thing, n); // ThingMultiplier is an Iterator
let new_things = things.clone().into_iter().flat_map(create_new_multiplier);
things.extend(new_things);
}
This is purely visual. I do not like to imbricate too much the stuff.
There should not be a performance difference between defining the closure before it's used versus defining and using it it directly. There is a type system difference — the compiler doesn't fully know how to infer types in a closure that isn't immediately called.
In code:
let create_new_multiplier = |thing| ThingMultiplier::new(thing, n);
things.clone().into_iter().flat_map(create_new_multiplier)
will be the exact same as
things.clone().into_iter().flat_map(|thing| {
ThingMultiplier::new(thing, n)
})
In general, there should not be a performance cost for using closures. This is what Rust means by "zero cost abstraction": the programmer could not have written it better themselves.
The compiler converts a closure into implementations of the Fn* traits on an anonymous struct. At that point, all the normal compiler optimizations kick in. Because of techniques like monomorphization, it may even be faster. This does mean that you need to do normal profiling to see if they are a bottleneck.
In your particular example, yes, extend can get inlined as a loop, containing another loop for the flat_map which in turn just puts ThingMultiplier instances into the same stack slots holding n and thing.
But you're barking up the wrong efficiency tree here. Instead of wondering whether an allocation of a small struct holding two fields gets optimized away you should rather wonder how efficient that clone is, especially for large inputs.
In Python you can do something like this:
if isinstance("hello", basestring):
print "hello is a string"
else:
print "Not a string"
My question is can this kind of code be recreated or emulated using Rust ? If it is possible, is this kind of checking necessary or useful in Rust ?
Python is dynamically typed. When you write for example a function def foo(x):, the caller can choose to give a value of any type as the parameter x. That’s why Python has isinstance(), so that you can check when it’s important.
Rust is statically typed. Any variable in the code has a type that is known at compile-time. For functions parameters you have to write it explicitly: fn foo(x: String) {. For local variables you can write it: let x: String = something(); or leave it to the compiler’s type inference to figure out: let x = something(); based on other information (here based on the return type of something()). Sometimes there is not enough context for type inference and you have to write an explicit type annotation.
If everything has a known type, an isinstance function that returns true or false doesn’t make sense. So Rust doesn’t quite have one.
Note that some form of dynamic typing is possible with trait objects and the Any trait:
http://doc.rust-lang.org/book/trait-objects.html
http://doc.rust-lang.org/std/any/trait.Any.html
So you can write:
fn foo(object: &Any) {
if object.is::<String>() {
// ...
}
}
object’s type is still static: it’s &Any. But it also represents a value of some other, arbitrary type. You can access that value with other Any methods such as downcast_ref.
Rust has a limited for a downcasting that can be provided by Any: you can Any to query whether the concrete type is X or Y.
The usefulness of the construct is rather limited though; Rust is a statically typed language so in most situation you:
either know the exact concrete type
or use a trait that has sufficient methods for your needs
still, Chris Morgan developed an AnyMap for example to store one value of each type, without knowing said types a-priori, which he then used to provide a typed interface to HTTP headers without restricting the set of headers to a known set.
I think the most similar paradigm in Rust is using the match keyword on enum types. For instance, in the std::net::IpAddr case, you can use matching to decide whether you are dealing with an Ipv4Addr or an Ipv6Addr.
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn main() {
do_something(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
do_something(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
}
fn do_something(v: IpAddr) {
match v {
IpAddr::V4(_x) => {
println!("I'm an IPv4 Address!");
}
IpAddr::V6(_x) => {
println!("I'm an IPv6 Address!");
}
}
}
Link to Rust Playground with working example
This has the advantage of having well-defined types everywhere, which is of course required as Rust is strongly typed. However, it does have a potential impact on how you arrange your data structures; thus this sort of behavior has to be planned in advance.
In Java, all objects inherit from java.lang.Object. In Go, all types/structs implement the empty interface interface {}. Is there any similar construct in the Rust language?
If the answer is no, what makes it unnecessary? Is it because all entities in Rust (except modules) can be parameterized by type? Does that remove the need for a common "supertype" or common Trait shared by all Rust entities/structs/enums?
Yes, there is a trait. It is std::any::Any.
From the docs:
The Any trait is implemented by all 'static types, and can be used for dynamic typing
The way I understand it, if not completely unnecessary, it is less necessary in Rust to have a base type.
Note that the introduction of parametric polymorphism (generics) has removed most of the use cases for Object also in java.
With Object you can implement a "generic" method that can work with any kind of java type. On the other hand, when you have an Object you can't do much with it... You have to cast it back to the actual subtype to use it.
For instance, the old non-generic version of java collections worked with Objects, This means you had to work like this (sample is straight from this post on Oracle's site):
LinkedList list = new LinkedList();
list.add(new Integer(1));
Integer num = (Integer) list.get(0);
add takes an Object (so you can use the collection with any type). But get returns an Object too, so you have to cast it back to Integer based on the knowledge that you (the programmer) have on what you originally inserted in the LinkedList. There's very little type safety here.
The new generic version of the same container, since java 1.5, is:
LinkedList<Integer> list = new LinkedList<Integer>();
list.add(new Integer(1));
Integer num = list.get(0);
Now you have a list of Integer, so add takes Integer and get returns Integer. Object is nowhere to be seen any more (although due to type erasure it's just there, barely hiding under the hood...)
Note that Go's main use of interface{} stems from the fact that Go does not have generics. The main pattern of use for interface{} is roughly the same. You use it when you have to work with multiple types and you sort-of cast it (although in a safer way and with a more elegant pattern) back to a more useful type before using it.
With that in mind, you could theoretically use Any in Rust in the same way (checking actual type and casting to it before using it). It's just that you will likely not find many cases where this might be useful.
The code below works on rustc 0.13-nightly (2015-01-01), although I'm sure there are better ways to write it...
use std::any::{Any, AnyRefExt};
// just a couple of random types
struct Foo {
a: f64
}
enum Bar {
Baz(int),
Qux
}
fn main() {
// create an array of things that could have any type
let anything = &[&1i as &Any, &Foo{a: 2.} as &Any, &Bar::Qux as &Any];
// iterate and print only the integer
for &any in anything.iter() {
if any.is::<int>() { // check if type is int
match any.downcast_ref::<int>() { // cast to int
Some(a) => { println!("{}", *a); }
None => panic!()
}
// or just (shorter version of the match)
println!("{}", *any.downcast_ref::<int>().unwrap());
}
}
}