Tuple elements may have side-effects, and some of them may depend on others. Consider this program:
fn main() {
let mut v = vec![1, 2];
match (v.pop(), v.pop()) {
(Some(z), Some(y)) => println!("y = {}, z = {}", y, z),
_ => unreachable!(),
}
}
Does it output y = 1, z = 2 or y = 2, z = 1? A few rounds on the Rust Playground suggests the former on stable 1.32.0, but maybe it would change if I ran it more times, recompiled the compiler, changed compiler versions, etc.
Is there a documented commitment or at least intention to maintain a particular order of evaluation for tuples (e.g. depth-first and left-to-right)?
Yes, the order of evaluation for tuples is guaranteed to be left-to-right (which also implies depth-first, as the value must be fully constructed).
Unfortunately, this is never stated explicitly anywhere that I can find, but can be inferred from Rust's strong backwards compatibility guarantees. Making a change to evaluation order would likely introduce far too much breakage to ever be seriously considered.
I'd also expect that the optimizer is allowed to make changes when safe to do so. For example, if the expressions in the tuple have no side effects, then reordering them is invisible to the user.
See also:
Rust tuple: evaluation order: left -> right?
The Rust Reference: Expressions
Related
Is there an easy way to find all of the even numbers and move them all to the end of vector? The order doesn't matter, all that matters is that evens were moved to the end. However, it would be nice if order was preserved.
For example: [1, 2, 3, 4, 5] => [1, 3, 5, 2, 4]
I'd like to have the signature pub fn move_by_filter(nums: &mut Vec<i32>).
I tried to filter and combine vector slices, but I am running into a problem combining array slices:
let evens = nums.iter().filter(|&&i| i % 2 == 0).collect::<Vec<_>>();
let odds = nums.iter().filter(|&&i| i % 2 != 0).collect::<Vec<_>>();
// then I want to do something like: nums = odds.push(evens)
This doesn't push them to the end of the vector.
I'm not sure if it is the best approach since I have to use iter() twice (which is O(N) + O(N) I think, but would like to do it in one operation if possible)
The easiest solution is to sort the vector with a custom sort key:
pub fn sort_by_parity(nums: &mut [i32]) {
nums.sort_by_key(|&x| x % 2 == 0);
}
Rust's standard sort algorithm is stable, so this will preserve the original order of the odd and even numbers.
The closure passed to create sort keys evaluates to false for odd numbers and to true for even numbers. This makes sure that all odd numbers are sorted before the even numbers.
Instead of accepting a mutable reference to a vector, this function accepts a mutable slice reference, which is more generic.
The runtime of this approach is O(n log n), which is not optimal for in-place partitioning. You can achieve linear runtime, O(n), e.g. using the partition() method:
pub fn partition_by_parity(nums: &mut [i32]) {
let (even, odd): (Vec<_>, Vec<_>) = nums.iter().partition(|&x| x % 2 == 0);
nums[..odd.len()].copy_from_slice(&odd);
nums[odd.len()..].copy_from_slice(&even);
}
The runtime difference between the two approaches is unlikely to matter in practice.
If you don't require to preserve the original order of the odd and even elements, you can partition the slice in place in linear time, without needing an additional buffer. Rust Nightly offers the unstable partition_in_place() method for this purpose, but it's not too difficult to implement yourself – it's basically the partitioning step in Quicksort.
Consider this example:
fn main() {
let v: Vec<i32> = vec![1, 2, 3, 4, 5];
let b: i32 = (&v[2]) * 4.0;
println!("product of third value with 4 is {}", b);
}
This fails as expected as float can't be multiplied with &i32.
error[E0277]: cannot multiply `{float}` to `&i32`
--> src\main.rs:3:23
|
3 | let b: i32 = (&v[2]) * 4.0;
| ^ no implementation for `&i32 * {float}`
|
= help: the trait `std::ops::Mul<{float}>` is not implemented for `&i32`
But when I change the float to int, it works fine.
fn main() {
let v: Vec<i32> = vec![1, 2, 3, 4, 5];
let b: i32 = (&v[2]) * 4;
println!("product of third value with 4 is {}", b);
}
Did the compiler implement the operation between &i32 and i32?
If yes, how is this operation justified in such a type safe language?
Did the compiler implement the operation between &i32 and i32?
Yes. Well, not the compiler, but rather the standard library. You can see the impl in the documentation.
If yes, how is this operation justified in such a type safe language?
"Type safe" is not a Boolean property, but rather a spectrum. Most C++ programmers would say that C++ is type safe. Yet, C++ has many features that automatically cast between types (constructors, operator T, taking references of values, ...). When designing a programming language, one has to balance the risk of bugs (when introducing convenient type conversions) with the inconvenience (when not having them).
As an extreme example: consider if Option<T> would deref to T and panic if it was None. That's the behavior of most languages that have null. I think it's pretty clear that this "feature" has led to numerous bugs in the real world (search term "billion dollar mistake"). On the other hand, let's consider what bugs could be caused by having &i32 * i32 compile. I honestly can't think of any. Maaaybe someone wanted to multiply the raw pointer of one value with an integer? Rather unlikely in Rust. So since the chance of introducing bugs with this feature is very low, but it is convenient, it was decided to be implemented.
This is always something the designers have to balance. Different languages are in a different position on this spectrum. Rust would likely be considered "more typesafe" than C++, but not doubt, there are even "more typesafe" languages than Rust out there. In this context, "more typesafe" just meant: decisions leaned more towards "inconvenience instead of potential bugs".
I think you may be confusing &i32 from rust with &var from C.
In C,
int var = 5;
int newvar = &var * 4; /* this would be bad code,
should not multiply an address by an integer!
Of course, C will let you. */
the '&' operator returns the address of the variable 'var'.
However, in rust, the '&' operator borrows use of the variable var.
In Rust,
let var: i32 = 5;
assert_eq!(&var * 8, 40);
This works, because &var refers to 5, not to the address of var. Note that in C, the & is an operator. In Rust, the & is acting as part of the type of the variable. Hence, the type is &i32.
This is very confusing. If there were more characters left on standard keyboard, i am sure the designers would have used a different one.
Please see the book and carefully follow the diagrams. The examples in the book use String, which is allocated on the heap. Primitives, like i32 are normally allocated on the stack and may be completely optimized away by the compiler. Also, primitives are frequently copied even when reference notation is used, so that gets confusing. Still, I think it is easier to look at the heap examples using String first and then to consider how this would apply to primitives. The logic is the same, but the actual storage and optimization my be different.
It’s very simple actually: Rust will automatically dereference references for you. It’s not like C where you have to dereference a pointer yourself. Rust references are very similar to C++ references in this regard.
In this snippet from Hyper's example, there's a bit of code that I've annotated with types that compiles successfully:
.map_err(|x: std::io::Error| -> hyper::Error {
::std::convert::From::<std::io::Error>::from(x)
})
The type definition of From::from() seems to be fn from(T) -> Self;
How is it that what seems to be a std::io::Error -> Self seems to return a hyper::Error value, when none of the generics and arguments I give it are of the type hyper::Error?
It seems that some sort of implicit type conversion is happening even when I specify all the types explicitly?
Type information in Rust can flow backwards.
The return type of the closure is specified to be hyper::Error. Therefore, the result of the block must be hyper::Error, therefore the result of From::from must be hyper::Error.
If you wanted to, you could use ...
<hyper::Error as ::std::convert::From>::<std::io::Error>::from(x)
... which would be the even more fully qualified version. But with the closure return type there, it's unnecessary.
Type inference has varying degrees.
For example, in C++ each literal is typed, and only a fully formed type can be instantiated, therefore the type of any expression can be computed (and is). Before C++11, this led to the compiler giving an error message: You are attempting to assign a value of type X to a variable of type Y. In C++11, auto was introduced to let the compiler figure out the type of the variable based on the value that was assigned to it.
In Java, this works slightly differently: the type of a variable has to be fully spelled out, but in exchange when constructing a type the generic bits can be left out since they are deduced from the variable the value is assigned to.
Those two examples are interesting because type information does not flow the same way in both of them, which hints that there is no reason for the flow to go one way or another; there are however technical constraints aplenty.
Rust, instead, uses a variation of the Hindley Milner type unification algorithm.
I personally see Hindley Milner as a system of equation:
Give each potential type a name: A, B, C, ...
Create equations tying together those types based on the structure of the program.
For example, imagine the following:
fn print_slice(s: &[u32]) {
println!("{:?}", s);
}
fn main() {
let mut v = Vec::new();
v.push(1);
print_slice(&v);
}
And start from main:
Assign names to types: v => A, 1 => B,
Put forth some equations: A = Vec<C> (from v = Vec::new()), C = B (from v.push(1)), A = &[u32] OR <A as Deref>::Output = &[u32] OR ... (from print_slice(&v),
First round of solving: A = Vec<B>, &[B] = &[u32],
Second round of solving: B = u32, A = Vec<u32>.
There are some difficulties woven into the mix because of subtyping (which the original HM doesn't have), however it's essentially just that.
In this process, there is no consideration for going backward or forwarded, it's just equation solving either way.
This process is known as Type Unification and if it fails you get a hopefully helpful compiler error.
I have the following code, in which fac return (MyType, OtherType):
let l = (-1..13).map(|x| {
fac(x).0
}).collect::<Vec<MyType>>();
It works, but I'm throwing away the OtherType values. So I decided to use .unzip, like this:
let (v, r) = (-1..13).map(|x| {
fac(x)
}).unzip();
let l = v.collect::<Vec<MyType>>();
let q = r.collect::<Vec<OtherType>>();
But type inference fails with:
error: the type of this value must be known in this context
let l = v.collect::<Vec<Literal>>();
^~~~~~~~~~~~~~~~~~~~~~~~~~~
let q = r.collect::<Vec<OtherType>>();
^~~~~~~~~~~~~~~~~~~~~~~~~~~
The thing is: I don't know or care what is the concrete type of the iterators (and I would suppose the compiler could infer them, as shown in the first snippet). How to satisfy the compiler in this case?
Also, I would prefer to restructure the code - I don't like to separately call .collect() on both v and r. Ideally I would continue the method chain after .unzip(), returning two Vecs in that expression.
.unzip() doesn't return iterators — it acts like two parallel collect! You can in fact collect the two pieces to different kinds of collections, but let's use vectors for both in this example:
// Give a type hint to determine the collection type
let (v, r): (Vec<MyType>, Vec<OtherType>) = (-1..13).map(|x| {
fac(x)
}).unzip();
It is done this way to be as simple and transparent as possible. Returning two iterators instead would need them to share a common state, a complexity that rust's iterator library prefers to avoid.
I have still quite a long way to go in learning Rust, but I find the way values and references are used to be inconsistent. This may be born from my own ignorance of the language.
For example, this works:
let x = (1..100).find(|a| a % 2 == 0);
But let x = (1..100).find(|a| a > 50); does not. I am not sure - why though?
Using let x = (1..100).find(|&a| a > 50); fixes the error, but then I thought using &a is like asking for reference of element from the range and hence following should work, but it does not:
let x = (1..100).find(|&a| *a > 50);
Again no idea why!
but then I thought using &a is like asking for reference of element from the range
This is the wrong part of your reasoning. Using & in pattern does exactly the opposite - it implicitly dereferences the matched value:
let &a = &10;
// a is 10, not &10 or &&10
As you probably already know, find() accepts a closure which satisfies FnMut(&T) -> bool, that is, this closure accepts a reference to each element of the iterator, so if you write (1..100).find(|a| ...), a will be of type &i32.
let x = (1..100).find(|a| a % 2 == 0) works because arithmetic operators are overloaded to work on references, so you can apply % to a reference and it still would be able to compile.
Comparison operators are not overloaded to handle references, and so you need to get an i32 from &i32. This could be done in two ways, first, like you already did:
let x = (1..100).find(|&a| a > 50)
Here we use & patterns to implicitly dereference the function argument. It is equivalent to this one:
let x = (1..100).find(|a| { let a = *a; a > 50 })
Another way would be to dereference the argument explicitly:
let x = (1..100).find(|a| *a > 50)
I thought using &a is like asking for reference of element from the range
Sometimes & is used as an operator, and sometimes it is used as a pattern match. For the closure parameter (|&a|), it is being used as a pattern match. This means that the variable a will be automatically dereferenced when it is used. It is also equivalent to do
let x = (1..100).find(|a| *a > 50);
Non-trivial patterns usually destructure something, i.e., break something into its components. This usually mirrors some construction syntax, so it looks very similar but is actually the inverse. This dualism applies to records, to tuples, to boxes (once those are properly implemented), and also to references:
The expression &x creates a reference to whatever x evaluates to. Here, the & turns a value of type T into one of type &T.
The pattern &a, on the other hand, eliminates the reference, so a is bound to what is behind the reference (note that a could also be another, more complicated pattern). Here, the & goes from a &T value to a T value.
The closures in your examples are all of of type &i32 -> bool1. So they accept a reference to an integer, and you can either work with that reference (which you do in the first example, which works because arithmetic operators are overloaded for references too) or you can use the pattern &a. In the latter case, a is a i32 (compare the general explanation above, substitute i32 for T), so of course you can't dereference it further.
1 This is not actually a real type, but it's close enough for our purposes.