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
Related
A very common pattern I have to deal with is, I am given some raw byte data. This data can represent an array of floats, 2D vectors, Matrices...
I know the data is compact and properly aligned. In C usually you would just do:
vec3 * ptr = (vec3*)data;
And start reading from it.
I am trying to create a view to this kind of data in rust to be able to read and write to the buffer as follows:
pub trait AccessView<T>
{
fn access_view<'a>(
offset : usize,
length : usize,
buffer : &'a Vec<u8>) -> &'a mut [T]
{
let bytes = &buffer[offset..(offset + length)];
let ptr = bytes.as_ptr() as *mut T;
return unsafe { std::slice::from_raw_parts_mut(ptr, length / size_of::<T>()) };
}
}
And then calling it:
let data: &[f32] =
AccessView::<f32>::access_view(0, 32, &buffers[0]);
The idea is, I should be able to replace f32 with vec3 or mat4 and get a slice view into the underlying data.
This is crashing with:
--> src/main.rs:341:9
|
341 | AccessView::<f32>::access_view(&accessors[0], &buffer_views, &buffers);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
|
= note: cannot satisfy `_: AccessView<f32>`
How could I use rust to achieve my goal? i.e. have a generic "template" for turning a set of raw bytes into a range checked slice view casted to some type.
There are two important problems I can identify:
You are using a trait incorrectly. You have to connect a trait to an actual type. If you want to call it the way you do, it needs to be a struct instead.
Soundness. You are creating a mutable reference from an immutable one through unsafe code. This is unsound and dangerous. By using unsafe, you tell the compiler that you manually verified that your code is sound, and the borrow checker should blindly believe you. Your code, however, is not sound.
To part 1, #BlackBeans gave you a good answer already. I would still do it a little differently, though. I would directly imlement the trait for &[u8], so you can write data.access_view::<T>().
To part 2, you at least need to make the input data &mut. Further, make sure they have the same lifetime, otherwise the compiler might not realize that they are actually connected.
Also, don't use &Vec<u8> as an argument; in general, use slices (&[u8]) instead.
Be aware that with all that said, there still is the problem of ENDIANESS. The behavior you will get will not be consistent between platforms. Use other means of conversion instead if that is something you require. Do not put this code in a generic library, at max use it for your own personal project.
That all said, here is what I came up with:
pub trait AccessView {
fn access_view<'a, T>(&'a mut self, offset: usize, length: usize) -> &'a mut [T];
}
impl AccessView for [u8] {
fn access_view<T>(&mut self, offset: usize, length: usize) -> &mut [T] {
let bytes = &mut self[offset..(offset + length)];
let ptr = bytes.as_ptr() as *mut T;
return unsafe { std::slice::from_raw_parts_mut(ptr, length / ::std::mem::size_of::<T>()) };
}
}
impl AccessView for Vec<u8> {
fn access_view<T>(&mut self, offset: usize, length: usize) -> &mut [T] {
self.as_mut_slice().access_view(offset, length)
}
}
fn main() {
let mut data: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8];
println!("{:?}", data);
let float_view: &mut [f32] = data.access_view(2, 4);
float_view[0] = 42.0;
println!("{:?}", float_view);
println!("{:?}", data);
// println!("{:?}", float_view); // Adding this would cause a compiler error, which shows that we implemented lifetimes correctly
}
[1, 2, 3, 4, 5, 6, 7, 8]
[42.0]
[1, 2, 0, 0, 40, 66, 7, 8]
I think you didn't understood exactly what traits are. Traits represent a characteristic of a type, for instance, since I know the size at compile-time of u32 (32 bits), u32 implements the marker trait Sized, noted u32: Sized. A more feature-complete trait could be the Default one: if there is a "default" way of building of type T, then we can implement Default for it, so that now there is a standard default way of building it.
In your example, you are using a trait as a namespace for functions, ie you could simply have
fn access_view<'a, T>(
offset: usize,
length: usize,
buffer: &'a [u8]
) -> &'a mut T
{
let bytes = &buffer[offset..offset+length];
let ptr = bytes.as_ptr() as *mut T;
unsafe {
std::slice::from_raw_parts_mut(ptr, length / size_of::<T>()
}
}
Or, if you want to put it as a trait:
trait Viewable {
fn access_view<'a>(
offset: usize,
length: usize,
buffer: &'a [u8],
) -> &'a mut [Self]
{
let bytes = &buffer[offset..offset+length];
let ptr = bytes.as_ptr() as *mut T;
unsafe {
std::slice::from_raw_parts_mut(ptr, length / size_of::<T>()
}
}
}
Then implement it:
impl<T> Viewable for T {}
Or, again, differently
trait Viewable {
fn access_view<'a>(
offset: usize,
length: usize,
buffer: &'a [u8],
) -> &'a mut [Self];
}
impl<T> Viewable for T {
fn access_view<'a>(
offset: usize,
length: usize,
buffer: &'a [u8],
) -> &'a mut [Self]
{
let bytes = &buffer[offset..offset+length];
let ptr = bytes.as_ptr() as *mut T;
unsafe {
std::slice::from_raw_parts_mut(ptr, length / size_of::<T>()
}
}
}
Although all this way to structure the code will somehow produce the same result, it doesn't mean they're equivalent. Maybe you should learn a little bit more about traits before using them.
Also, your code, as is, really seems unsound, in the sense that you make a call to an unsafe function without any checking (ie. what if I call it with random nonsense in buffer?). It doesn't mean it is (we don't have access to the rest of your code), but you should be careful about that: Rust is not C.
Finally, your error simply comes from the fact that it's impossible for Rust to find out which type T you are calling the associated method access_view of.
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.
I just started learning rust and I'm creatively trying somethings as I read "the Rust Book".
I know it is possible to create a generic method to get the largest element from an array, like the following:
fn largest<T: PartialOrd + Copy> (nums: &[T]) -> T {
let mut largest = nums[0];
for &el in nums {
if el > largest {
largest = el;
}
}
return largest;
}
And calling the main function like this:
fn main() {
let list: Vec<u32> = vec![1,7,4];
println!("{}", largest(&list)); // 7
}
How would I go doing the same thing but "extending" the array, like this:
fn main() {
let list: Vec<u32> = vec![1,7,4];
println!("{}", list.largest()); // 7
}
I guess the final question is: if it is possible, would it be a bad practice? Why?
I tried something like this, but didn't manage to figure out how to implement the "Largeble" trait returning the T:
pub trait Largeble {
fn largest(&self);
}
impl<T: Copy + PartialOrd + Display> Largeble for Vec<T> {
fn largest(&self) {
let mut largest = match self.get(0) {
Some(&el) => el,
None => panic!("Non Empty array expected")
};
for &el in self {
if el > largest {
largest = el;
}
}
println!("{}", largest);
// return largest;
}
}
You need to make the Largeable trait return a T from the Vec<T>, which you can do with an associated type:
use std::fmt;
pub trait Largeble {
type Output;
fn largest(&self) -> Self::Output;
}
impl<T: Copy + PartialOrd + fmt::Display> Largeble for Vec<T> {
type Output = T;
fn largest(&self) -> T {
let mut largest = match self.get(0) {
Some(&el) => el,
None => panic!("Non Empty array expected")
};
for &el in self {
if el > largest {
largest = el;
}
}
largest
}
}
println!("{}", vec![1, 2, 3, 2].largest()); // prints "3"
Traits like Largeable are usually called extension traits, since they extend existing items with new features. Using extension traits to extend items in existing libraries is common in the Rust ecosystem. It's common to suffix the names of extensions with Ext (so a collection of additional methods for Vec would be called VecExt). A popular use of extension traits is the itertools library, which provides a trait that adds additional useful methods to Iterator in the standard library.
How would I go doing the same thing but "extending" the array
Sure, your code snippet was close, you can create a trait and implement it on types.
pub trait Largeble<T>
where
T: Ord,
{
fn largest(&self) -> Option<&T>;
}
impl<T> Largeble<T> for Vec<T>
where
T: Ord,
{
fn largest(&self) -> Option<&T> {
// Iterator already has a method for getting max which simplifies things
self.iter().max()
}
}
Alternatively, you can make T an associated type which may be better suited to this example.
You can run this code in the Rust Playground.
Would it be a bad practice? Why?
Nope, it's definitely not bad practise. It is a very common way to developing in Rust and can be very powerful. Your trait needs to be in scope for you to be able to call .largest(), so it does not pollute anything.
Additionally, if you have multiple methods with the same name from different traits, you can provide a longer syntax to specify the exact trait you want to use: Largest::<u32>::largest(&list).
I tried something like this, but didn't manage to figure out how to implement the "Largeble" trait returning the T.
Your code was mostly correct, but your largest method didn't return anything. That's why the trait needs a generic T, to specify that you will return T.
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)
I have an &[u8] and would like to turn it into an &[u8; 3] without copying. It should reference the original array. How can I do this?
As of Rust 1.34, you can use TryFrom / TryInto:
use std::convert::TryFrom;
fn example(slice: &[u8]) {
let array = <&[u8; 3]>::try_from(slice);
println!("{:?}", array);
}
fn example_mut(slice: &mut [u8]) {
let array = <&mut [u8; 3]>::try_from(slice);
println!("{:?}", array);
}
They arrayref crate implements this.
Here's an example, you can of course use it in different ways:
#[macro_use]
extern crate arrayref;
/// Get the first 3 elements of `bytes` as a reference to an array
/// **Panics** if `bytes` is too short.
fn first3(bytes: &[u8]) -> &[u8; 3] {
array_ref![bytes, 0, 3]
}
EDIT: TryFrom/TryInto has been stabilized as of Rust 1.34. Please see #shepmaster's answer for an updated method.
Just to re-emphasize, this can't be done without unsafe code because you don't know until runtime that the slice has three elements in it.
fn slice_to_arr3<T>(slice: &[T]) -> Option<&[T; 3]> {
if slice.len() == 3 {
Some(unsafe { &*(slice as *const [T] as *const [T; 3]) })
} else {
None
}
}
This can't be generic over the length of the array until const generics are implemented.