Lifetime trouble when slice filtering - rust

I want to optionally filter a slice without changing the type. The following approach fails:
use std::collections::HashSet;
fn main() {
let include: Option<HashSet<i8>> = Some(HashSet::new());
let y: &[i8] = &[1, 2, 3];
let z: &[i8] = match include {
Some(set) => {
let filtered: Vec<i8> = y.iter().filter(|&i| set.contains(i)).map(|&i| i).collect();
filtered.as_slice()
},
None => y
};
println!("{:?}", z);
}
with error
error: `filtered` does not live long enough
--> <anon>:10:9
|
9 | filtered.as_slice()
| -------- borrow occurs here
10 | },
| ^ `filtered` dropped here while still borrowed
...
14 | }
| - borrowed value needs to live until here
-- playground
I understand that filtered doesn't live long enough because the slice is simply a reference into it. I have tried to copy or clone or otherwise make a static version of the resulting filtered.as_slice(), but nothing compiles.
Declaring filtered outside of the definition of z won't work, because it requires set to do the filtering.
I expect this dance of converting to an iterator, then to a vector, and then to a slice is probably not idiomatic. So I'm interested to know of a better approach.

Declaring filtered outside of the definition of z won't work, because it requires set to do the filtering.
I don't understand why not. In the statement filtered.as_slice(), you borrow from filtered, so you must make sure filtered lives longer than the variable you assign it to (z):
use std::collections::HashSet;
fn main() {
let mut h = HashSet::new();
h.insert(2);
let exclude: Option<HashSet<i8>> = Some(h);
let y: &[i8] = &[1, 2, 3];
let filtered: Vec<i8>;
let z: &[i8] = match exclude {
Some(set) => {
filtered = y.iter().filter(|&i| !set.contains(i)).map(|&i| i).collect();
filtered.as_slice()
},
None => y
};
println!("{:?}", z); // outputs [1, 3]
}
Note I inverted the logic in filter.

Related

Dealing with clone

I'm trying to iterate over a VCF file and create vectors with the data to build a DataFrame.
However, the rust compilator is raising an error saying that the borrowed value does not live enough.
I'm cloning the value because, in that case, I'm borrowing the vcf record as immutable and as mutable at the same time.
I don't know how to proceed. Below there are the snippet of my code and the error.
use flate2::read::MultiGzDecoder;
use std::fs::File;
use std::io::BufReader;
mod vcf_reader;
use vcf::{VCFReader, U8Vec, VCFHeaderFilterAlt, VCFError, VCFRecord};
use vcf_reader::reader::VCFSamples;
use polars_core::prelude::*;
use std::time::Instant;
fn main() -> Result<()> {
let now = Instant::now();
let mut reader = VCFReader::new(BufReader::new(File::open(
"C:\\Users\\Desktop\\rust_lectures\\vcf_reader\\vcf_reader\\*.vcf"
)?)).unwrap();
// prepare VCFRecord object
let mut vcf_record = VCFRecord::new(reader.header());
// read one record
let mut chromosome = Vec::new();
let mut position = Vec::new();
let mut id = Vec::new();
let mut reference = Vec::new();
let mut alternative = Vec::new();
let result: bool = reader.next_record(&mut vcf_record).unwrap();
loop {
let row = vcf_record.clone();
if result == false {
let df = df!(
"Chromosome" => chromosome,
"Position" => position,
"Id" => id,
"Reference" => reference,
"Alternative" => alternative,
);
println!("{:?}", df);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
return Ok(())
} else {
chromosome.push(String::from_utf8_lossy(&row.chromosome));
position.push(&row.position);
id.push(String::from_utf8_lossy(&row.id[0]));
reference.push(String::from_utf8_lossy(&row.reference));
alternative.push(String::from_utf8_lossy(&row.alternative[0]));
}
reader.next_record(&mut vcf_record).unwrap();
}
error[E0597]: `row.chromosome` does not live long enough
--> src\main.rs:44:53
|
44 | chromosome.push(String::from_utf8_lossy(&row.chromosome));
| ----------------------------------------^^^^^^^^^^^^^^^^^^^^--
| | |
| | borrowed value does not live long enough
| borrow later used here
...
50 | }
| - `row.chromosome` dropped here while still borrowed
I'm cloning the value because
The problem is that you're cloning the record but then you're storing references into your arrays. Since those are references to the cloned record, they only live until the end of the block.
So either:
move the attributes out of the clone (essentially explode it)
or rather than working with a copy of the record, copy individual fields out of the base vcf_record
Either way you're also misusing from_utf8_lossy: it always returns a reference-ish, because it avoids allocating if the input is valid utf8 (in that case it essentially just returns a reference to the original data).

Moving ownership to a std::io::Chain

I have a Reader that I want to prepend some bytes to, creating a Chain. Ideally I'd want to do this:
use std::io::{Chain, Read};
fn thingify<R: Read>(r: R) -> Chain<[u8; 3], R> {
let mut arr = [1u8, 2u8, 3u8];
// Modify arr here
return arr.chain(r);
}
But that throws a compiler error:
error[E0308]: mismatched types
--> test.rs:7:12
|
3 | fn thingify<R: Read>(r: R) -> Chain<[u8; 3], R>
| ----------------- expected `std::io::Chain<[u8; 3], R>` because of return type
...
7 | return arr.chain(r);
| ^^^^^^^^^^^^ expected array of 3 elements, found &[u8]
|
= note: expected type `std::io::Chain<[u8; 3], _>`
found type `std::io::Chain<&[u8], _>`
From what I understand, this seems to be because Read is implemented for slices rather than arrays, and somehow my array decays to a slice here.
But when I change the array in the return type to a slice and give it an explicit lifetime like so:
use std::io::{Chain, Read};
fn thingify<'a, R: Read>(r: R) -> Chain<&'a [u8], R> {
let arr = [1u8, 2u8, 3u8];
// Modify arr here
return arr.chain(r);
}
I just get another compiler error instead:
error[E0515]: cannot return value referencing local variable `arr`
--> test.rs:19:12
|
19 | return arr.chain(r);
| ---^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `arr` is borrowed here
How can I transfer ownership of my array to the Chain so that I can return it? Is that simply not doable with a [u8]?
Because Read is implemented for &'_ [u8] but not for [u8; 3], the compiler automatically converts your array into a reference slice. This mean your array must be valid as long as the slice live so as long as the Chain live.
There are several solutions, you could ask to the caller a mutable slice, you could make it static if you want to be able to mutate it, if you don't you can make it const, if you need to resize it you need a Vec, etc...
use std::io::{stdin, Chain, Read};
fn a<R: Read>(arr: &mut [u8; 3], r: R) -> Chain<&[u8], R> {
arr.copy_from_slice(&[1, 2, 3]);
arr.chain(r)
}
fn b<R: Read>(r: R) -> Chain<&'static [u8], R> {
const ARR: [u8; 3] = [1, 2, 3];
ARR.chain(r)
}
fn main() {
let mut arr = [0; 3];
println!("{:?}", a(&mut arr, stdin()));
println!("{:?}", b(stdin()));
}
See:
Is there any way to return a reference to a variable created in a function?

Why does a variable holding the result of Vec::get_mut not need to be mutable?

I have the following code:
fn main() {
let mut vec = Vec::new();
vec.push(String::from("Foo"));
let mut row = vec.get_mut(0).unwrap();
row.push('!');
println!("{}", vec[0])
}
It prints out "Foo!", but the compiler tells me:
warning: variable does not need to be mutable
--> src/main.rs:4:9
|
4 | let mut row = vec.get_mut(0).unwrap();
| ----^^^
| |
| help: remove this `mut`
Surprisingly, removing the mut works. This raises a few questions:
Why does this work?
Why doesn't this work when I use vec.get instead of vec.get_mut, regardless of whether I use let or let mut?
Why doesn't vec work in the same way, i.e. when I use let vec = Vec::new(), why can't I call vec.push()?
vec.get_mut(0) returns an Option<&mut String>, so when you unwrap that value you will have a mutable borrow of a String. Remember, that a let statement's left side is using pattern matching, so when your pattern is just a variable name you essentially say match whatever is on the right and call it name. Thus row matches against &mut String so it already is mutable.
Here's a much simpler and more straightforward example to illustrate the case (which you can try in the playground):
fn main() {
let mut x = 55i32;
dbg!(&x);
let y = &mut x; // <-- y's type is `&mut i32`
*y = 12;
dbg!(&x);
}

Mutable reference to an item using 'find' in Rust

How can I get a mutable reference to an item found in a vector?
I've tried the following which works if I don't make the iterator mutable using .iter():
fn main() {
let mut vec = vec![1, 2, 3, 4];
let mut wrong = -1;
let working = match vec.iter().find(|&c| *c == 2) {
Some(c) => c,
None => &wrong
};
println!("Result: {}", working);
}
But when I try to get a mutable reference using a mutable iterator .iter_mut(),
fn main() {
let mut vec = vec![1, 2, 3, 4];
let mut wrong = -1;
let mut error = match vec.iter_mut().find(|&c| *c == 2) {
Some(c) => c,
None => &mut wrong
};
println!("Result: {}", error);
}
I get the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:5:48
|
5 | let mut error = match vec.iter_mut().find(|&c| *c == 2) {
| ^-
| ||
| |hint: to prevent move, use `ref c` or `ref mut c`
| cannot move out of borrowed content
I also tried to make the type in the closure mutable with |&mut c| but that gives the following error:
error[E0308]: mismatched types
--> src/main.rs:5:48
|
5 | let mut error = match vec.iter_mut().find(|&mut c| *c == 2) {
| ^^^^^^ types differ in mutability
|
= note: expected type `&&mut {integer}`
found type `&mut _`
= help: did you mean `mut c: &&&mut {integer}`?
Rust's .find passes the callback the type &Self::Item, and since you are using .iter_mut(), you've created an iterator where each item is &mut T. That means the type passed to your find callback is &&mut T. To get that to typecheck, you can do either
vec.iter_mut().find(|&&mut c| c == 2)
or
vec.iter_mut().find(|c| **c == 2)
with the second one being preferable.
The error you are getting is because the middle-ground you've chosen by using &c would set c to a value of &mut T, and one of Rust's big rules is that multiple things can't own a mutable reference to an item at the same time. Your non-mutable case works because you are allowed to have multiple immutable references to an item.

How do I concatenate two slices in Rust?

I want to take the x first and last elements from a vector and concatenate them. I have the following code:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
vec![v.iter().take(3), v.iter().skip(l-3)];
}
This gives me the error
error[E0308]: mismatched types
--> <anon>:4:28
|
4 | vec![v.iter().take(3), v.iter().skip(l-3)];
| ^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Take`, found struct `std::iter::Skip`
<anon>:4:5: 4:48 note: in this expansion of vec! (defined in <std macros>)
|
= note: expected type `std::iter::Take<std::slice::Iter<'_, u64>>`
= note: found type `std::iter::Skip<std::slice::Iter<'_, u64>>`
How do I get my vec of 1, 2, 3, 8, 9, 10? I am using Rust 1.12.
Just use .concat() on a slice of slices:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
let first_and_last = [&v[..3], &v[l - 3..]].concat();
println!("{:?}", first_and_last);
// The output is `[0, 1, 2, 7, 8, 9]`
}
This creates a new vector, and it works with arbitrary number of slices.
(Playground link)
Ok, first of all, your initial sequence definition is wrong. You say you want 1, 2, 3, 8, 9, 10 as output, so it should look like:
let v = (1u64 .. 11).collect::<Vec<_>>();
Next, you say you want to concatenate slices, so let's actually use slices:
let head = &v[..3];
let tail = &v[l-3..];
At this point, it's really down to which approach you like the most. You can turn those slices into iterators, chain, then collect...
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
...or make a vec and extend it with the slices directly...
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
...or extend using more general iterators (which will become equivalent in the future with specialisation, but I don't believe it's as efficient just yet)...
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
...or you could use Vec::with_capacity and push in a loop, or do the chained iterator thing, but using extend... but I have to stop at some point.
Full example code:
fn main() {
let v = (1u64 .. 11).collect::<Vec<_>>();
let l = v.len();
let head = &v[..3];
let tail = &v[l-3..];
println!("head: {:?}", head);
println!("tail: {:?}", tail);
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
println!("v2: {:?}", v2);
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
println!("v3: {:?}", v3);
// Explicit type to help inference.
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
println!("v4: {:?}", v4);
}
You should collect() the results of the take() and extend() them with the collect()ed results of skip():
let mut p1 = v.iter().take(3).collect::<Vec<_>>();
let p2 = v.iter().skip(l-3);
p1.extend(p2);
println!("{:?}", p1);
Edit: as Neikos said, you don't even need to collect the result of skip(), since extend() accepts arguments implementing IntoIterator (which Skip does, as it is an Iterator).
Edit 2: your numbers are a bit off, though; in order to get 1, 2, 3, 8, 9, 10 you should declare v as follows:
let v = (1u64 .. 11).collect::<Vec<_>>();
Since the Range is left-closed and right-open.

Resources