How to access the element at variable index of a tuple? - rust

I'm writing a function to read vectors from stdin, and here is what I have so far:
fn read_vector() -> (i64, i64, i64) {
let mut vec = (0, 0, 0);
let mut value = String::new();
for i in 0..3 {
io::stdin().read_line(&mut value).expect("Failed to read line");
vec.i = value.trim().parse().expect("Failed to read number!"); // error!
}
}
However, the annotated line contains an error:
error: no field `i` on type `({integer}, {integer}, {integer})`
--> src/main.rs:13:13
|
13 | vec.i = value.trim().parse().expect("Failed to read number!");
| ^
Reading the documentation entry doesn't reveal any get, or similar function.
So, is there any way to get the ith value of a tuple?

There isn't a way built in the language, because variable indexing on a heterogeneous type like a tuple makes it impossible for the compiler to infer the type of the expression.
You could use a macro that unrolls a for loop with variable indexing for a tuple if it is really, really necessary though.
If you are going to be using homogeneous tuples that require variable indexing, why not just use a fixed-length array?

So, is there any way to get the ith value of vec?
No, there isn't. Since tuples can contain elements of different types, an expression like this wouldn't have a statically-known type in general.
You could consider using an array instead of a tuple.

While there are no built-in methods to extract the i-th value for non-constant i, there exist crates like tuple to implement dynamic indexing of a homogeneous tuple.
extern crate tuple;
...
*vec.get_mut(i).unwrap() = value.trim().parse().expect("!");
(But, as #fjh mentioned, it is far better to operate on an array [i64; 3] instead of a tuple (i64, i64, i64).)

Related

How to define an array or vector that can contain any primitive data type in rust?

How to define an array or vector that can contain any primitive data type in rust?
let v: [std::any::Any] = <something>;
Solution 1 (faster + safer)
enum Data {
Usize(usize),
U8(u8),
U16(u16),
U32(u16),
U64(u16),
Isize(isize),
I8(i8),
I16(i16),
I32(i16),
I64(i16),
F32(f32),
F64(f64),
Bool(bool),
Char(char),
}
fn main() {
let v: [Data; 3] = [Data::Usize(1), Data::Bool(true), Data::Char('a')];
}
Solution 2 (flexible, supports almost any type)
use std::any::Any;
fn main() {
let v: [Box<dyn Any>; 3] = [Box::new(1), Box::new('a'), Box::new(true)];
}
You can't. The very point of an array or a Vec is that they are homogenous, which also means each member needs to be the same size. But when given a u8 and a u128 - which are both std::any::Any - their sizes are not the same. One, therefore, needs a layer of indirection e.g. via [Box<dyn std::any::Any>; _] or Vec<Box<std::any::Any>>.
Contrary to Javascript, Rust is a strongly typed language. This means that any variable has a single type that is known at compile-time. This includes vector or array elements. You can work around this limitation by wrapping your data in an enum that will keep track of the actual type of the contained value at runtime.
Note that I strongly advise against using Any until you have lots of experience with Rust and know that your are using it for the right reason (and in particular not just to reproduce a pattern found in scripting languages.

How to explicitly annotate the type of an array literal

I have this Rusqlite code:
use rusqlite::types::ToSql;
// ... normal Rusqlite initialisation code ...
let mut statement = tx.prepare("INSERT INTO table VALUES (?1, ?2)")?;
let params: &[&dyn ToSql] = &[
&0u32,
&"hello",
];
statement.execute(params)?;
The ?1 parameter is an INTEGER and the ?2 parameter is TEXT. This compiles, however if I move the params into the function call it does not compile:
statement.execute(&[
&0u32,
&"hello",
])?;
This gives the following error for &hello.
mismatched types
expected type `&u32`
found reference `&&'static str`
It seems like it infers the type for the array literal based on the type of the first element. What is the syntax for explicitly setting the type of the array?
You annotate the type of an array literal the same way as any other type, by writing the type after the variable name, separated by a colon:
let array: [u8; 5] = [1, 2, 3, 4, 5];
That's not your problem. Your problem is that you are creating a heterogeneous array, one where the types of each element differ. The first element is a reference to an integer, the second a string, etc. You need to perform the case to the trait object more eagerly:
let x = [&42 as &dyn Display, &true];
For rusqlite specifically, use the params! macro to do this for you:
use rusqlite::params; // 0.23.1
fn main() {
let x = params![&42, &true];
}
See also:
How to create a vector of boxed closures in Rust?
Use Option::map to Box::new a trait object does not work
How to coerce a Vec of structs to a Vec of trait objects?
Why does Rusqlite reject the Option type when executing a query?
Cannot call rusqlite's query because it expects the type &[&rusqlite::types::ToSql]

Why do I get "no method named push found for type Option" with a vector of vectors?

I tried to use a String vector inside another vector:
let example: Vec<Vec<String>> = Vec::new();
for _number in 1..10 {
let mut temp: Vec<String> = Vec::new();
example.push(temp);
}
I should have 10 empty String vectors inside my vector, but:
example.get(0).push(String::from("test"));
fails with
error[E0599]: no method named `push` found for type `std::option::Option<&std::vec::Vec<std::string::String>>` in the current scope
--> src/main.rs:9:20
|
9 | example.get(0).push(String::from("test"));
| ^^^^
Why does it fail? Is it even possible to have an vector "inception"?
I highly recommend reading the documentation of types and methods before you use them. At the very least, look at the function's signature. For slice::get:
pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where
I: SliceIndex<[T]>,
While there's some generics happening here, the important part is that the return type is an Option. An Option<Vec> is not a Vec.
Refer back to The Rust Programming Language's chapter on enums for more information about enums, including Option and Result. If you wish to continue using the semantics of get, you will need to:
Switch to get_mut as you want to mutate the inner vector.
Make example mutable.
Handle the case where the indexed value is missing. Here I use an if let.
let mut example: Vec<_> = std::iter::repeat_with(Vec::new).take(10).collect();
if let Some(v) = example.get_mut(0) {
v.push(String::from("test"));
}
If you want to kill the program if the value is not present at the index, the shortest thing is to use the index syntax []:
example[0].push(String::from("test"));

Way to specify a static slice of variable length

Let's say I have a function with following signature:
fn validate(samples: &[(&str, &[Token])])
Where Token is a custom enum.
I would like to be able to write something along those lines:
let samples = vec![
("a string", &[Token::PLUS, Token::MINUS, Token::PLUS]),
("another string", &[Token::MUL]),
];
validate(&samples);
But code like this yields mismatched types compile error:
error: mismatched types:
expected `&[(&str, &[Token])]`,
found `&collections::vec::Vec<(&str, &[Token; 3])>`
Is it possible to somehow convert the version with static length (&[Token; 3]) to a static slice (&[Token])?
In other words, I would like to be able to specify a static slice in similar way I specify &str, as some kind of "slice literal".
Or I am doing it completely wrong?
EDIT:
In short, I would like to find a syntax that creates an array with static lifetime (or at least a lifetime that is as long as the samples vector's one), and returns slice of it.
Something similar to how strings work, where just typing "a string" gives me reference of type &'static str.
EDIT2:
#Pablo's answer provides pretty good solution to my particular problem, although it is not exactly what I have meant at first.
I guess that the exact thing I have in mind might not be possible, so I will just accept that one for now, unless something more in lines of my initial idea come around.
In short, I would like to find a syntax that creates an array with
static lifetime (or at least a lifetime that is as long as the samples
vector's one), and returns slice of it.
You’d want something like this:
fn sliced(array: [Token; 3]) -> &'static [Token] { unimplemented!() }
So you could use it like this in your example:
let samples: Vec<(&str, &[Token])> = vec![
("a string", sliced([Token::PLUS, Token::MINUS, Token::PLUS])),
// ...
But there are two problems with it. The first and most glaring is that you can’t get a static reference out of a function which doesn’t take in a static reference (in which case it would just return it).
Therefore, since you want a slice at least as long-lived as your array, either you declare a const/static slice (which requires also a const/static declaration of its array), or you declare the array with a let statement first, and then make the slice. (This is what is done at my first alternative, below.) If you create the array inside a use of vec!, together with its slice, the array end its life with vec!, rendering the slice invalid. As an illustration, consider this, which fails due to the same reason:
fn main() {
let slice;
{
let array: [u8; 3] = [1,2,3];
slice = &array;
}
}
The second problem with the sliced function is that its input array has a fixed size, and you’d want to work generically over arrays of arbitrary size. However, this is currently not supported by Rust[1]. You have to work with slices in order to deal with arrays of arbitrary size.
One possibility, then, is to do the following [playpen]:
enum Token {
PLUS,
MINUS,
MUL,
}
fn validate(samples: &[(&str, &[Token])]) {
unimplemented!()
}
fn main() {
let tokens_0 = [Token::PLUS, Token::MINUS, Token::PLUS];
let tokens_1 = [Token::MUL];
let samples: Vec<(&str, &[Token])> = vec![
("a string", &tokens_0),
("another string", &tokens_1),
];
validate(&samples);
}
There are two changes here with respect to your code.
One, this code relies on implicit coercing of an array ([T; N]) as a slice (&[T]) by taking a reference to it. This is demanded by the declaration of samples as being of type Vec<(&str, &[Token])>. This is later satisfied, when using vec!, by passing references to the arrays, and thus eliciting the appropriate coercions.
Two, it creates the arrays of Token before using the vec! macro, which guarantees that they’ll live enough to be referenced from within the Vec it creates, keeping these references valid after vec! is done. This is necessary after resolving the previous type mismatch.
Addendum:
Or, for convenience, you may prefer to use a Vec instead of slices. Consider the following alternative [playpen]:
enum Token {
PLUS,
MINUS,
MUL,
}
fn validate<T>(samples: &[(&str, T)]) where
T: AsRef<[Token]>
{
let _: &[Token] = samples[0].1.as_ref();
unimplemented!()
}
fn main() {
let samples: Vec<(&str, Vec<Token>)> = vec![
("a string", vec![Token::PLUS, Token::MINUS, Token::PLUS]),
("another string", vec![Token::MUL]),
];
validate(&samples);
}
In this case, the AsRef<[Token]> bound on the second element of the tuple accepts any type from which you may take a &[Token], offering an as_ref() method which returns the expected reference. Vec<Token> is an example of such kind of type.
[1] “Rust does not currently support generics over the size of an array type.” [source]
Note: this answer is not valid in this particular situation because the arrays pointed by the nested slices cannot outlive the vector because they are only allocated for the duration of their respective expressions, therefore slices to them can't be stored in the vector.
The proper way would be to either hoist slices to the upper level and put them before the vector, or to use an entirely different structure, e.g. nested Vecs. Examples of all of these are provided in Pablo's answer.
You need to do this:
let samples = vec![
("a string", &[Token::PLUS, Token::MINUS, Token::PLUS] as &[_]),
("another string", &[Token::MUL] as &[_]),
];
validate(&samples);
Rust automatically converts references to arrays (&[T; n]) to slices (&[T]) when the target type is known, but in this case type inference doesn't work well because of the necessary deref coercion, so the compiler can't deduce that you need a slice instead of array and can't insert the appropriate conversion, thus you need to specify the type explicitly.
Also, there is no such thing as "static slice". The closest entity would be a slice with static lifetime, &'static [T], but as far as I remember, this is not the case of it.

Why does the argument for the find closure need two ampersands?

I have been playing with Rust by porting my Score4 AI engine to it - basing the work on my functional-style implementation in OCaml. I specifically wanted to see how Rust fares with functional-style code.
The end result: It works, and it's very fast - much faster than OCaml. It almost touches the speed of imperative-style C/C++ - which is really cool.
There's a thing that troubles me, though — why do I need two ampersands in the last line of this code?
let moves_and_scores: Vec<_> = moves_and_boards
.iter()
.map(|&(column,board)| (column, score_board(&board)))
.collect();
let target_score = if maximize_or_minimize {
ORANGE_WINS
} else {
YELLOW_WINS
};
if let Some(killer_move) = moves_and_scores.iter()
.find(|& &(_,score)| score==target_score) {
...
I added them is because the compiler errors "guided" me to it; but I am trying to understand why... I used the trick mentioned elsewhere in Stack Overflow to "ask" the compiler to tell me what type something is:
let moves_and_scores: Vec<_> = moves_and_boards
.iter()
.map(|&(column,board)| (column, score_board(&board)))
.collect();
let () = moves_and_scores;
...which caused this error:
src/main.rs:108:9: 108:11 error: mismatched types:
expected `collections::vec::Vec<(u32, i32)>`,
found `()`
(expected struct `collections::vec::Vec`,
found ()) [E0308]
src/main.rs:108 let () = moves_and_scores;
...as I expected, moves_and_scores is a vector of tuples: Vec<(u32, i32)>. But then, in the immediate next line, iter() and find() force me to use the hideous double ampersands in the closure parameter:
if let Some(killer_move) = moves_and_scores.iter()
.find(|& &(_,score)| score==target_score) {
Why does the find closure need two ampersands? I could see why it may need one (pass the tuple by reference to save time/space) but why two? Is it because of the iter? That is, is the iter creating references, and then find expects a reference on each input, so a reference on a reference?
If this is so, isn't this, arguably, a rather ugly design flaw in Rust?
In fact, I would expect find and map and all the rest of the functional primitives to be parts of the collections themselves. Forcing me to iter() to do any kind of functional-style work seems burdensome, and even more so if it forces this kind of "double ampersands" in every possible functional chain.
I am hoping I am missing something obvious - any help/clarification most welcome.
This here
moves_and_scores.iter()
gives you an iterator over borrowed vector elements. If you follow the API doc what type this is, you'll notice that it's just the iterator for a borrowed slice and this implements Iterator with Item=&T where T is (u32, i32) in your case.
Then, you use find which takes a predicate which takes a &Item as parameter. Sice Item already is a reference in your case, the predicate has to take a &&(u32, i32).
pub trait Iterator {
...
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where P: FnMut(&Self::Item) -> bool {...}
... ^
It was probably defined like this because it's only supposed to inspect the item and return a bool. This does not require the item being passed by value.
If you want an iterator over (u32, i32) you could write
moves_and_scores.iter().cloned()
cloned() converts the iterator from one with an Item type &T to one with an Item type T if T is Clone. Another way to do it would be to use into_iter() instead of iter().
moves_and_scores.into_iter()
The difference between the two is that the first option clones the borrowed elements while the 2nd one consumes the vector and moves the elements out of it.
By writing the lambda like this
|&&(_, score)| score == target_score
you destructure the "double reference" and create a local copy of the i32. This is allowed since i32 is a simple type that is Copy.
Instead of destructuring the parameter of your predicate you could also write
|move_and_score| move_and_score.1 == target_score
because the dot operator automatically dereferences as many times as needed.

Resources