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.
Related
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! 😃
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).
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];
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);
}
If I have a 2D array in D, I know that I can create 1D slices along rows as follows:
auto one_dim_arr=two_dim_arr[i][0..$]
is there a simple way to make a 1D slice along columns? Something that does what one might think
auto one_dim_arr=two_dim_arr[0..$][j]
would do?
Here is what a user-created type for this might look like:
// Demo
void main()
{
int[3][3] arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// simple creation
auto middleColumn = verticalSlice(arr, 1);
assert(middleColumn[1] == 5);
// iteratable
foreach (i, v; middleColumn)
assert(v == 2+i*3);
// still a slice - writing will change original array
middleColumn[1] = 17;
assert(arr[1][1] == 17);
// sliceable itself
auto center = middleColumn[1..2];
center[0] = 42;
assert(arr[1][1] == 42);
// get a normal array with .dup
int[] copyOfMiddleColumn = middleColumn.dup;
}
// Implementation
struct StepSlice(T)
{
T* ptr;
size_t length, step;
T opIndex(size_t index)
in { assert(index<length); }
body { return ptr[step*index]; }
void opIndexAssign(T value, size_t index)
in { assert(index<length); }
body { ptr[step*index] = value; }
StepSlice!T opSlice(size_t start, size_t end)
in { assert(start<=end && end<=length); }
body { return StepSlice!T(ptr+start*step, end-start, step); }
int opApply(int delegate(ref T) dg)
{
int result = 0;
for (size_t i=0; i<length; i++)
{
result = dg(ptr[i*step]);
if (result)
break;
}
return result;
}
int opApply(int delegate(ref size_t, ref T) dg)
{
int result = 0;
for (size_t i=0; i<length; i++)
{
result = dg(i, ptr[i*step]);
if (result)
break;
}
return result;
}
T[] dup()
{
T[] result = new T[length];
for (size_t i=0; i<length; i++)
result[i] = ptr[i*step];
return result;
}
}
StepSlice!T verticalSlice(T, size_t W)(T[W][] arr, size_t column)
{
return StepSlice!T(arr[0].ptr+column, arr.length, W);
}
I think it's missing range primitives, but still a good starting point.
With std.range.stride:
import std.range;
// Demo
void main()
{
int[3][3] arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// simple creation
auto middleColumn = verticalSlice(arr, 1);
assert(middleColumn[1] == 5);
// iteratable
uint i;
foreach (v; middleColumn)
assert(v == 2+(i++)*3);
// still a slice - writing will change original array
middleColumn[1] = 17;
assert(arr[1][1] == 17);
// sliceable itself
auto center = middleColumn[1..2];
center[0] = 42;
assert(arr[1][1] == 42);
// get a normal array with array()
int[] copyOfMiddleColumn = array(middleColumn);
}
// Implementation
auto verticalSlice(T, size_t W)(T[W][] arr, size_t column)
{
T* start = arr[0].ptr+column;
return stride(start[0..W*arr.length], W);
}
No, it's not possible. For this to work, D slices would need to have a step. It's possible to create a custom type which works similarly to a slice (e.g. std.algorithm.map).
Note that your suggested syntax above would compile fine, but not have the effect you're looking for.
If your input is a T[][] (i.e. a dynamic array of dynamic arrays) and you want the same as output, you can allocate a new 'outer' array and fill it with slices of the inner arrays. This will result in an O(n) op where as a normal slice is a O(1) op. The coding is left as an exercise for the reader.