Rust - How to initialize array of structs containing HashSet field? - arrays

I have Instance struct with HashSet field for storing nonordered numbers with easy removing and adding with duplicates control.
Then there is another struct called Matrix, which holds an two-dimensional array of Instance struct.
I create the new() method in trait for Instance to use in array initialization, but using this method gives error during compilation, saying that HashSet does not implement the Copy trait.
Here is the code:
#![allow(unused)]
use std::collections::HashSet;
struct Instance {
ids: HashSet<u8>,
value: u8,
}
trait IsInstance {
fn new() -> Instance;
}
impl IsInstance for Instance {
fn new() -> Instance {
Instance {
ids: [1, 2, 3, 5].iter().cloned().collect(),
value: 0,
}
}
}
/*
Line below is commented due to error:
error[E0204]: the trait `Copy` may not be implemented for this type
--> src/main.rs:26:6
|
5 | ids: HashSet,
| ---------------- this field does not implement `Copy`
...
26 | impl Copy for Instance {}
| ^^^^
*/
//impl Copy for Instance {}
impl Clone for Instance {
fn clone(&self) -> Instance {
Instance {
ids: self.ids,
value: self.value,
}
}
}
struct Matrix {
instances: [[Instance; 4]; 4],
status: u8,
}
fn main() {
let mut m = Matrix {
instances: [[Instance::new(); 4]; 4],
status: 0,
};
}
Compiling this gives error:
error[E0507]: cannot move out of `self.ids` which is behind a shared reference
--> src/main.rs:42:18
|
42 | ids: self.ids,
| ^^^^^^^^ move occurs because `self.ids` has type `std::collections::HashSet<u8>`, which does not implement the `Copy` trait
error[E0277]: the trait bound `Instance: std::marker::Copy` is not satisfied
--> src/main.rs:60:22
|
60 | instances : [[Instance::new(); 4]; 4],
| ^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `Instance`
|
= note: the `Copy` trait is required because the repeated element will be copied
error[E0277]: the trait bound `[Instance; 4]: std::marker::Copy` is not satisfied
--> src/main.rs:60:21
|
60 | instances : [[Instance::new(); 4]; 4],
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `[Instance; 4]`
|
= note: the `Copy` trait is required because the repeated element will be copied
Is there any way to properly initialize this array? I clearly missed something, but didn't find HashSet copy implementation working with arrays. Maybe there is another way to implement this?

I clearly missed something, but didn't find HashSet copy implementation working with arrays.
That's because there isn't one! Copy means that a type is copyable bit-for-bit. That cannot be the case of a type like HashSet<T>. However what HashSet<T> is is default-initializable. Combine with the fact that arrays (under 32 elements for now) are also default-initializable, you can use the following:
let mut m = Matrix {
instances: Default::default(),
status: 0,
};
if you add
impl Default for Instance {
fn default() -> Self {
Self::new()
}
}
(Permalink to the playground)

Related

Lifetime and borrowing const arrays [duplicate]

This question already has an answer here:
Getting 'Missing Lifetime specifier' error
(1 answer)
Closed last year.
I'm starting to study Rust and I admit, I have some problems with lifetimes and borrowing. On even days I think I got it and on odd days I get bitten ! Too much C or C++ ? Or perhaps too old ?
;-)
The following code does not compile:
use core::f64::consts::PI;
pub struct TableOscillator {
sample_rate: u32,
table: &[f64],
}
impl TableOscillator {
pub fn new(sample_rate: u32, table: &[f64]) -> TableOscillator {
TableOscillator { sample_rate, table }
}
// Other methods ...
}
fn main() {
const SAMPLE_RATE: u32 = 96000;
let table: [f64; 1024];
for i in 0..table.len() {
let phase = (2.0 * PI * i as f64) / (table.len() as f64);
table[i] = phase.sin();
}
// At this point I would like "table" to be constant and usable by all the following oscillators.
let osc1 = TableOscillator::new(SAMPLE_RATE, &table);
let osc2 = TableOscillator::new(SAMPLE_RATE, &table);
// ...
}
Here is the compiler message:
error[E0106]: missing lifetime specifier
--> src/main.rs:5:12
|
5 | table: &[f64],
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
3 ~ pub struct TableOscillator<'a> {
4 | sample_rate: u32,
5 ~ table: &'a [f64],
|
A bit of explanation: the different oscillators use their "table" members (only read no write).
What is the Rust idiomatic way to fix that ?
Thanks !
In order to do this you need to annotate the lifetime for TableOscillator to tell Rust that the reference needs to live as long as the struct:
pub struct TableOscillator<'a> {
sample_rate: u32,
table: &'a [f64],
}
osc1 and osc2 cannot outlive table, because they're borrowing it.
See Validating References with Lifetimes.

How do I implement a non-consuming IntoIterator for a struct with an underlying collection?

Let's say I have a struct that has a collection, such as a Vec as one of its data members:
struct MyCollection {
data: Vec<i32>
}
I want the user of MyCollection to be able to iterate over its data without direct access to the Vec itself, like so:
let x = MyCollection{data:vec![1, 2, 3, 4, 5]};
for i in &x {
//...
}
However, I'm struggling with implementing the necessary Trait IntoIterator for the non-consuming version with &x. I have successfully implemented the consuming version:
impl std::iter::IntoIterator for MyCollection {
type Item = i32;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
return self.data.into_iter();
}
}
However, this is only usable as follows:
for i in x {
println!("{:?}", i);
}
which consumes x. Cloning the data is possible, but quite expensive, so I'd like to avoid that.
Here is what I have so far for the non-consuming version, which I based on the source implementation of std::vec::Vec:
impl<'a> std::iter::IntoIterator for &'a MyCollection {
type Item = &'a i32;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
return self.data.into_iter();
}
}
which produces the following compile error:
error: mismatched types
error: expected &i32, found i32
note: expected type `std::vec::IntoIter<&i32>`
found type `std::vec::IntoIter<i32>`
error: expected `std::vec::IntoIter<&i32>` because of return type
I have also tried removing the &'a of the type Item since in my case, the elements of data are Copyable, but this yields the following:
error: cannot move out of `self.data` which is behind a shared reference
error: move occurs because `self.data` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
I understand the function wants an IntoIter of a vector to references, but I'm unsure how to give it one efficiently. I'm new to Rust, so I'd much appreciate some clarity on the concern. Bonus points if you can also tell me how to create a mutable iterator for write access in the same fashion.
First, you should use slice type, your user shouldn't have to know that you inner type is vector. Then, your problem is that you must not use IntoIter type, but Iter type directly.
Simple example:
struct MyCollection {
data: Vec<i32>,
}
impl<'a> std::iter::IntoIterator for &'a MyCollection {
type Item = <std::slice::Iter<'a, i32> as Iterator>::Item;
type IntoIter = std::slice::Iter<'a, i32>;
fn into_iter(self) -> Self::IntoIter {
self.data.as_slice().into_iter()
}
}
fn main() {
let x = MyCollection {
data: vec![1, 2, 3, 4, 5],
};
for i in &x {
println!("{}", i);
}
}

Passing an immutable reference when a mutable reference exists

I have a for loop that iterates over a slice of Point structs. The Points will have some fields modified in the loop, so the function containing the loop requires a mutable reference to the slice.
The problem arises when I need to pass a (immutable) reference pointing to the slice to a function within the for loop that iterates over the mutable reference:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut grid = vec![];
grid.push(Point { x: 10, y: 10 });
grid.push(Point { x: -1, y: 7 });
calculate_neighbors(&mut grid);
}
fn calculate_neighbors(grid: &mut [Point]) {
for pt in grid.iter_mut() {
pt.x = nonsense_calc(grid);
}
}
#[allow(unused_variables)]
fn nonsense_calc(grid: &[Point]) -> i32 {
unimplemented!();
}
Playground
error[E0502]: cannot borrow `*grid` as immutable because it is also borrowed as mutable
--> src/main.rs:18:30
|
17 | for pt in grid.iter_mut() {
| ---------------
| |
| mutable borrow occurs here
| mutable borrow used here, in later iteration of loop
18 | pt.x = nonsense_calc(grid);
| ^^^^ immutable borrow occurs here
The compiler complains that grid cannot be borrowed as immutable, because a mutable borrow already exists. This is correct, and I can see the problem it is trying to prevent, but how do I achieve what I need to do? Ideally, I do not have to create a copy of the grid, as this can be expensive.
A solution to avoid borrow the array for the iteration would be to use indexes:
fn calculate_neighbors(grid: &mut [Point]) {
for i in 0..grid.len() {
grid[i].x = nonsense_calc(grid);
}
}

Prevent 'static lifetime requirement in Rc<Trait>

Where is the 'static lifetime requirement coming from when using a trait type in std::rc::Rc and how can I prevent it? E.g. when trying to compile this code
trait Handler{}
fn add(a: std::rc::Rc<Handler>) {
}
fn main() {
add(0);
}
rust reports
error[E0308]: mismatched types
--> test.rs:7:9
...
= note: expected type `std::rc::Rc<Handler + 'static>`
found type `{integer}`
NOTE: the error itself is expected; I am just interested in the Handler + 'static diagnostic output. The Real program creates instances of a trait type, stores them into a HashMap and runs a type specific function on it. It fails to compile with
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
at this place.
Second example
The following code is more real-world and demonstrates the issue perhaps better:
trait Handler {
}
struct SomeHandler<'a>(&'a u32);
impl <'a> SomeHandler<'a> {
fn new(a: &'a u32) -> std::rc::Rc<Handler> {
std::rc::Rc::new(SomeHandler(a))
}
}
impl <'a> Handler for SomeHandler<'a> {
}
fn main() {
let a: u32;
SomeHandler::new(&a);
}
It fails with
8 | std::rc::Rc::new(SomeHandler(a))
| ^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::rc::Rc<Handler + 'static>
found std::rc::Rc<Handler>
Why explicit lifetimes do not work
The simple demo might be fixed by adding an explicit lifetime (e.g. Rc<Handler + 'a>). Unfortunately, this is not an option (nor trying to make anything 'static) because real code is intended to look like
struct PollRegistry {
...
handlers: std::collections::HashMap<mio::Token, std::rc::Weak<PollHandler>>,
}
impl PollRegistry {
fn register<'a>(&mut self, handler: &std::rc::Rc<PollHandler>,
interest: mio::Ready, opts: mio::PollOpt)
-> std::io::Result<()> {
....
self.handlers.insert(token, std::rc::Rc::downgrade(handler));
}
}
and methods in PollHandler specializations create and own other PollHandler specializations which are registered in the registry by these methods.
rustc 1.27.1

How to default-initialize a struct containing an array in Rust?

What is the recommended way to declare a struct that contains an array, and then create a zero-initialized instance?
Here is the struct:
#[derive(Default)]
struct Histogram {
sum: u32,
bins: [u32; 256],
}
and the compiler error:
error[E0277]: the trait bound `[u32; 256]: std::default::Default` is not satisfied
--> src/lib.rs:4:5
|
4 | bins: [u32; 256],
| ^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `[u32; 256]`
|
= help: the following implementations were found:
<[T; 14] as std::default::Default>
<&'a [T] as std::default::Default>
<[T; 22] as std::default::Default>
<[T; 7] as std::default::Default>
and 31 others
= note: required by `std::default::Default::default`
If I attempt to add the missing initializer for the array:
impl Default for [u32; 256] {
fn default() -> [u32; 255] {
[0; 256]
}
}
I get:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/lib.rs:7:5
|
7 | impl Default for [u32; 256] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate
|
= note: the impl does not reference any types defined in this crate
= note: define and implement a trait or new type instead
Am I doing something wrong?
Rust does not implement Default for all arrays because it does not have non-type polymorphism. As such, Default is only implemented for a handful of sizes.
You can, however, implement a default for your type:
impl Default for Histogram {
fn default() -> Histogram {
Histogram {
sum: 0,
bins: [0; 256],
}
}
}
Note: I would contend that implementing Default for u32 is fishy to start with; why 0 and not 1? or 42? There is no good answer, so no obvious default really.
I'm afraid you can't do this, you will need to implement Default for your structure yourself:
struct Histogram {
sum: u32,
bins: [u32; 256],
}
impl Default for Histogram {
#[inline]
fn default() -> Histogram {
Histogram {
sum: 0,
bins: [0; 256],
}
}
}
Numeric types have nothing to do with this case, it's more like problems with fixed-size arrays. They still need generic numerical literals to support this kind of things natively.
If you're sure to initialize every field with zero, this would work:
impl Default for Histogram {
fn default() -> Histogram {
unsafe { std::mem::zeroed() }
}
}
Indeed, at the time of writing, support for fixed-length arrays is still being hashed out in the standard library:
https://github.com/rust-lang/rust/issues/7622

Resources