Pass array of arrays (or slice of slices) in Rust - arrays

I need to pass a reference to an array of references to arrays (or slice of slices) to the following function in Rust
const LNGTH: usize = 5;
fn swap_array<T>(x: &mut [&[T; LNGTH]]) {
let temp = x[1];
x[1] = x[0];
x[0] = temp;
}
The problem is that it seems I have to specify an array length for the "inner" arrays (here: LNGTH).
So, the following code works fine:
fn main() {
let x_array: [i32; LNGTH] = [5,2,8,9,1];
let x_other: [i32; LNGTH] = [6,7,6,7,6];
let mut y_array: [&[i32; LNGTH]; 2] = [&x_array, &x_other];
println!("before : {:?}", y_array);
swap_array(&mut y_array);
println!("after : {:?}", y_array);
}
But if I change the signature of swap_array to fn swap_array<T>(x: &mut [&[T]]), I get the following error:
error[E0308]: mismatched types
--> src/main.rs:14:16
|
14 | swap_array(&mut y_array[..]);
| ^^^^^^^^^^^^^^^^ expected slice, found array of 5 elements
|
= note: expected type `&mut [&[_]]`
found type `&mut [&[i32; 5]]`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `tut_arrays`.
From the perspective of C, I would like to have a function that accepts arguments of type T**. A corresponding function in C would look like that
void swap_arrays(my_type ** x) {
my_type* temp = x[1];
x[1] = x[0];
x[0] = temp;
}

Here is a slice-of-slices version:
const LEN: usize = 5;
fn swap_array<T>(x: &mut [&[T]]) {
let temp = x[1];
x[1] = x[0];
x[0] = temp;
}
fn main() {
let x_array: [i32; LEN] = [5, 2, 8, 9, 1];
let x_other: [i32; LEN] = [6, 7, 6, 7, 6];
let mut y_array: [&[i32]; 2] = [&x_array, &x_other];
println!("before : {:?}", y_array);
swap_array(&mut y_array);
println!("after : {:?}", y_array);
}
You have to change the formal argument to slice of slices, and the elements of y_array must be slices, too (the latter is basically what the error message said).

Related

Nested for loop in rust, modifying elements in a vector

I am trying to create a nested loop in rust, that goes through a vector. Essentially, it looks like this:
fn main() {
let v = vec![1, 2, 3];
for i in &mut v {
for j in &mut v {
if i == j {
*i = *j + 1;
}
}
}
println!("{:?}", v);
}
However, this will not work; Rust cannot borrow as mutable more than once.
In my case, this the elements in the vector are structs that have non copy-able elements inside of them. How could rust go about doing something like this?
You'll have to use indexing in this case as a work around, which will create more localized borrows and satisfy the borrow checker:
fn main() {
let mut v = vec![1, 2, 3];
for i in 0..v.len() {
for j in 0..v.len() {
if v[i] == v[j] {
v[i] = v[j] + 1;
}
}
}
println!("{:?}", v);
}
Output:
[4, 4, 4]
Playground
Here, only v[i] is borrowed mutably for a moment while it is assigned the value of v[j] + 1.

How do I initialize flate2::GzEncoder outside of a loop?

This outputs the raw and GZip compressed length line by line as a way to estimate string complexity:
use std::fs::File;
use std::io::{BufReader, BufRead, Read};
use flate2::{read, Compression};
fn main() {
let mut f = File::open("/etc/passwd").unwrap();
let mut f = BufReader::new(f);
let mut _buf = vec![0u8; 100];
for line in f.lines() {
let l = line.unwrap();
let p = l.as_bytes().len();
let mut e = read::GzEncoder::new(l.as_bytes(), Compression::default());
let q = e.read(&mut _buf).unwrap();
println!("raw = {}, zip = {}", p, q);
}
}
I suspect that calling GzEncoder::new in every iteration might be expensive and want to move it outside the loop. How do I do that using flate2?

Removing adjacent values from vector

I made this function which should remove all adjacent values it finds from a vector.
fn remove_adjacent<T: std::cmp::PartialEq>(values: &mut Vec<T>, item: T) {
let mut offset = 0;
while let Some(idx) = values.iter().skip(offset).position(|n| *n == item) {
let length = values
.iter()
.skip(idx)
.position(|v| *v != item)
.unwrap_or(values.len() - idx);
if length > 1 {
values.drain(idx + 1..length + idx);
}
offset = idx + 1;
}
}
It works fine for vectors like
vec![2, 1, 3, 3, 3, 3, 3];
But not for vectors whose target element to be removed repeats after a non-target value, like
vec![2, 1, 3, 3, 3, 3, 3, 7, 3, 3, 3];
It should also remove the threes 3 values after 7, but instead it get stuck in an infinite loop. I'm not able find the error on my own, if anyone has tips on how to fix this I'll really appreciate it.
Example on Rust Playground
Everything in your code works correctly except getting the idx.
You can print out the idx and see what is wrong with it. Printing offset helps too.
fn remove_adjacent<T: std::cmp::PartialEq>(values: &mut Vec<T>, item: T) {
let mut offset = 0;
while let Some(idx) = values.iter().skip(offset).position(|n| *n == item) {
dbg!(idx, offset); // prints out nicely
let length = values
.iter()
.skip(idx)
.position(|v| *v != item)
.unwrap_or(values.len() - idx);
if length > 1 {
values.drain(idx + 1..length + idx);
}
offset = idx + 1;
}
}
You will notice that the idx is not always what you want.
This is happening becuase .position( counts not in values but in the iterator you get after .skip(offset).
I hope looking at the printed values and my clue guides you to fix the error on your own. Good luck! 😃

How to modify the last item of an array?

Since arr is borrowed as mutable, the length of arr can't be gotten by calling len(). I'm stuck here, what's the right way to do it?
fn double_last(arr: &mut[i32]) -> &i32 {
let last = &mut arr[arr.len() - 1]; // borrow checker error.
//let last = &mut arr[3]; // fine
*last *= 2;
last
}
fn main() {
let mut a = [1,2,3,4];
println!("{}", double_last(&mut a));
println!("{:?}", a);
}
If you only need the last, you can use std::slice::last_mut
fn double_last(arr: &mut[i32]) -> &i32 {
let last = arr.last_mut().unwrap();
*last *= 2;
last
}
This will hopefully be fixed with the introduction of non-lexical lifetimes and the accompanying changes soon into the future (seems like it could be solved?).
For now though, you can satisfy the borrow checker by splitting that calculation out:
let n = arr.len() - 1;
let last = &mut arr[n];

How to read a matrix from a txt file in Rust?

File example; square matrix; size of matrix after #
#3
1.1 -0.2 0.1
0.1 -1.2 -0.2
0.2 -0.1 1.1
Approximately so i would write it in C
double **A;
int i,j,size=0;
FILE *f=NULL;
f=fopen("input.txt","w");
fscanf(f,"#%d\n",&size);
A=(double**)malloc(size*sizeof(double*));
for(i=0;i<size;i++)
A[i]=(double*)malloc(size*sizeof(double));
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fscanf(f,"%lf",&A[i][j]);
}
}
fclose(f);
I tried to use the method "read_to_string" and parse String, but I'm confused by the conversion between String and str.
This is a naive translation of your code to Rust:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
// open the file
let mut f = BufReader::new(File::open("input.txt").unwrap());
// read the first line and extract the number from it
let mut num_line = String::new();
f.read_line(&mut num_line).unwrap();
let n: usize = num_line[1..].trim().parse().unwrap();
// preallocate the array and read the data into it
let mut arr = vec![vec![0f64; n]; n];
for (i, line) in f.lines().enumerate() {
for (j, number) in line.unwrap().split(char::is_whitespace).enumerate() {
arr[i][j] = number.trim().parse().unwrap();
}
}
println!("{:?}", arr);
}
There is more idiomatic way to perform the loop in Rust, though:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let mut f = BufReader::new(File::open("input.txt").unwrap());
let mut num_line = String::new();
f.read_line(&mut num_line).unwrap();
let n: usize = num_line[1..].trim().parse().unwrap();
let arr: Vec<Vec<f64>> = f.lines()
.take(n)
.map(|l| l.unwrap().split(char::is_whitespace)
.take(n)
.map(|number| number.parse().unwrap())
.collect())
.collect();
println!("{:?}", arr);
}
In fact, you don't even need the number of lines in advance to read the data if the format of your file is completely fixed:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let mut f = BufReader::new(File::open("input.txt").unwrap());
let mut s = String::new();
f.read_line(&mut s).unwrap();
let arr: Vec<Vec<f64>> = f.lines()
.map(|l| l.unwrap().split(char::is_whitespace)
.map(|number| number.parse().unwrap())
.collect())
.collect();
println!("{:?}", arr);
}

Resources