How to modify the last item of an array? - arrays

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];

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 to build a str in Rust [duplicate]

This question already has answers here:
How to handle "borrowed value does not live long enough" error when finding the longest substring of consecutive equal characters?
(2 answers)
Closed last month.
Suppose I have a char in the variable c and a positive int in the variable n. I want to build the str containing c occurring n times. How can I do it?
I tried building it as a String, and maybe I just got dizzy trying to read the documentation on strings, but I couldn't see how to convert it to a str. But then if I'm trying to just build it as a str directly then I couldn't see how to do that either.
For context, here is the full function I'm trying to implement. It takes a string and finds the longest sequence of consecutive characters (and breaks ties by taking the first that occurs).
pub fn longest_sequence(s: &str) -> Option<&str> {
if s.len() == 0 { return None; }
let mut current_c = s.as_bytes()[0] as char;
let mut greatest_c = s.as_bytes()[0] as char;
let mut current_num = 0;
let mut greatest_num = 0;
for ch in s.chars() {
if current_c == ch {
current_num += 1;
if current_num > greatest_num {
greatest_num = current_num;
greatest_c = current_c;
}
} else {
current_num = 1;
current_c = ch;
}
}
// Now build the output str ...
}
I think there are a couple of misconceptions about str vs String.
str can never exist alone. It is always used as &str (or Box<str> or *str, but in your case those shouldn't matter).
&str does not own any data. It is merely a reference to (parts of) another String.
String actually holds data.
So when you want to return data, use String; if you want to reference existing data, return &str.
There is no way to convert a local String to a &str. Somewhere the data has to be stored, and &str doesn't store it. (for completeness sake: Yes you could leak it, but that would create a permanent string in memory that will never go away again)
So in your case there are two ways:
Reference the input &str, because somewhere its data is already stored.
Return a String instead.
As a side note: do not do s.as_bytes()[0] as char, as it will not work with UTF8-strings. Rust strings are defined as UTF8.
Here is one possible solution:
pub fn longest_sequence(s: &str) -> Option<&str> {
let mut current_c = s.chars().next()?;
let mut current_start = 0;
let mut current_len = 0;
let mut greatest: &str = "";
let mut greatest_len = 0;
for (pos, ch) in s.char_indices() {
if current_c == ch {
current_len += 1;
} else {
if greatest_len < current_len {
greatest = &s[current_start..pos];
greatest_len = current_len;
}
current_len = 1;
current_c = ch;
current_start = pos;
}
}
if greatest_len < current_len {
greatest = &s[current_start..];
}
Some(greatest)
}
pub fn main() {
let s = "๐Ÿคช๐Ÿ˜๐Ÿ˜๐Ÿ˜๐Ÿ˜‰โ‚ฌโ‚ฌ๐Ÿคช๐Ÿคช";
let seq = longest_sequence(s);
println!("{:?}", seq);
}
Some("๐Ÿ˜๐Ÿ˜๐Ÿ˜")
Some explanations:
No need to check for empty string. s.chars().next()? does so automatically.
Use s.chars().next() instead of s.as_bytes()[0] as char, as the second one is not UTF8 compatible.
I explicitely store greatest_len instead of using greatest.len() because greatest.len() is also not UTF8 compatible as it gives you the size of the string in bytes, not in chars.
You stored the new largest string whenever a new char of the same value was found; I had to move it to the case where the char type changed (and once after the loop), because we don't yet know the end of the current char. Again, note that &s[current_start..current_start+current_len] wouldn't work, because &s[ .. ] wants indices in bytes, but current_len is in chars. So we need to wait for another char to know where the previous one ended.
Another solution, based on your code, would be:
pub fn longest_sequence(s: &str) -> Option<String> {
let mut current_c = s.chars().next()?;
let mut greatest_c = current_c;
let mut current_num = 0;
let mut greatest_num = 0;
for ch in s.chars() {
if current_c == ch {
current_num += 1;
if current_num > greatest_num {
greatest_num = current_num;
greatest_c = current_c;
}
} else {
current_num = 1;
current_c = ch;
}
}
// Build the output String
Some(std::iter::repeat(greatest_c).take(greatest_num).collect())
}
pub fn main() {
let s = "๐Ÿคช๐Ÿ˜๐Ÿ˜๐Ÿ˜๐Ÿ˜‰โ‚ฌโ‚ฌ๐Ÿคช๐Ÿคช";
let seq = longest_sequence(s);
println!("{:?}", seq);
}
Some("๐Ÿ˜๐Ÿ˜๐Ÿ˜")
To convert a String to &'static str you need to leak it like this:
fn leak(s: String) -> &'static str {
let ptr = s.as_str() as *const str;
core::mem::forget(s);
unsafe {&*ptr}
}
And char to String:
fn cts(c: char, n: usize) -> String {
(0..n)
.map(|_| c)
.collect()
}
So char to &'static str basically will look like this:
fn conv(c: char, n: usize) -> &'static str {
leak(cts(c, n))
}
I do not recommend to leak the String tho, just use it as is.

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?

Why is my rust slower than my C - memory manipulation?

I have two implmentations that yield the same Vec in the end, however my C implementation is about 2x as fast as the Rust implementation. Here are my implementations:
extern {
fn array_test(buffer: *mut u8, length: u32);
}
fn main() {
let mut vectest = Vec::<u8>::with_capacity(2 << 20);
unsafe { // using C function
vectest.set_len(2 << 20);
let start = SystemTime::now();
array_test(vectest.as_mut_ptr(), 2 << 20);
let end = SystemTime::now();
println!("C func took {:?}", end.duration_since(start));
};
let mut vectest2 = Vec::<u8>::with_capacity(2 << 20);
unsafe { // pure Rust
vectest2.set_len(2 << 20);
let start = SystemTime::now();
let vectest2_ptr = vectest2.as_mut_ptr();
for i in (0..2 << 20).step_by(4) {
*(vectest2_ptr.add(i) as *mut u32) = i as u32;
}
let end = SystemTime::now();
println!("Rust took {:?}", end.duration_since(start));
};
}
C function:
void array_test(char *buffer, unsigned int len) {
for (unsigned int i = 0; i < len; i += 4) {
(*(int *)(buffer+i)) = i;
}
}
One instance of time results from running cargo run --release is:
C func took Ok(440.692ยตs)
Rust took Ok(921.658ยตs)
About every time I run it the C finishes twice as fast.
Is this just due to poor Rust code code optimization on my part? If so, what could I do to close the gap between my C and my Rust?
If you re-write your loop:
for i in (0..2 << 20).step_by(4) {
*(vectest2_ptr.add(i) as *mut u32) = i as u32;
}
as
let mut i = 0;
while i <= (2 << 20) {
*(vectest2_ptr.add(i) as *mut u32) = i as u32;
i += 4;
}
the performance difference you're seeing goes away.
From a quick glance at the assembly on rust.godbolt.org (using -O as argument for rustc) it looks like rustc/llvm is able to vectorize (with SIMD) the loop when using the while variant, but it isn't applying that same optimization to your for loop.

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