Create an array with enum element size - arrays

I want to create an array of a size specified by an element in an enum, like so:
pub use self::Register::*;
enum Register {
Ip,
Sp,
NumRegs,
}
struct State {
val: int,
regs: [int; NumRegs as int],
running: bool,
}
But I get:
src/main.rs:19:11: 19:32 error: expected constant expr for array length: non-constant path in constant expr
src/main.rs:19 regs: [int; NumRegs as int],
I've tried using as int, among other solutions, and Googled around for some time, but have not found a solution. Incidentally, is this bad form in Rust?

Currently, I don't think Rust is capable of seeing that Enum::Variant is effectively a constant (I don't actually know that it is, either), so it cannot be used as the array length. More than that, I would say that it's strange to add an enum variant that isn't actually intended to be used.
For how I see your problem, I'd probably try to represent your registers as a struct:
struct Registers {
ip: u8,
sp: u8,
}
struct State {
val: u8,
regs: Registers,
running: bool,
}
fn main() {
let s = State {
val: 0,
regs: Registers { ip: 0, sp: 0 },
running: false,
};
}
Edit
If you'd like to get a register by name, how about we do that directly:
struct Registers {
ip: u8,
sp: u8,
}
impl Registers {
fn by_name(&self, name: &str) -> u8 {
match name {
"ip" => self.ip,
"sp" => self.sp,
_ => panic!("Unknown register '{}'", name),
}
}
fn by_name_mut(&mut self, name: &str) -> &mut u8 {
match name {
"ip" => &mut self.ip,
"sp" => &mut self.sp,
_ => panic!("Unknown register '{}'", name),
}
}
}
fn main() {
let mut r = Registers { ip: 0, sp: 0 };
println!("Instruction pointer: 0x{:02x}", r.by_name("ip"));
*r.by_name_mut("ip") += 1;
println!("Instruction pointer: 0x{:02x}", r.by_name("ip"));
}
Although that panic! right there is pretty ugly... I'd rather use an enum for that purpose. Let's do both the enum and the string:
use std::str::FromStr;
#[derive(Debug,Copy,Clone,PartialEq)]
enum Register {
Ip,
Sp,
}
impl FromStr for Register {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"ip" => Ok(Register::Ip),
"sp" => Ok(Register::Sp),
_ => Err(()),
}
}
}
struct Registers {
ip: u8,
sp: u8,
}
impl Registers {
fn by_name(&self, name: Register) -> u8 {
match name {
Register::Ip => self.ip,
Register::Sp => self.sp,
}
}
fn by_name_mut(&mut self, name: Register) -> &mut u8 {
match name {
Register::Ip => &mut self.ip,
Register::Sp => &mut self.sp,
}
}
}
fn main() {
let mut rs = Registers { ip: 0, sp: 0 };
let r: Register = "ip".parse().unwrap();
println!("Instruction pointer: 0x{:02x}", rs.by_name(r));
*rs.by_name_mut(r) += 1;
println!("Instruction pointer: 0x{:02x}", rs.by_name(r));
}
Now we have a nice clear separation between transitioning a string to our Register type, which means that only one part of our code has to deal with invalid register names (the call to unwrap).

Related

How to initialize an array with an iterator in Rust? [duplicate]

I'm having trouble initializing a fixed length array. My attempts so far all result in the same "use of possibly uninitialized variable: foo_array" error:
#[derive(Debug)]
struct Foo { a: u32, b: u32 }
impl Default for Foo {
fn default() -> Foo { Foo{a:1, b:2} }
}
pub fn main() {
let mut foo_array: [Foo; 10];
// Do something here to in-place initialize foo_array?
for f in foo_array.iter() {
println!("{:?}", f);
}
}
error[E0381]: use of possibly uninitialized variable: `foo_array`
--> src/main.rs:13:14
|
13 | for f in foo_array.iter() {
| ^^^^^^^^^ use of possibly uninitialized `foo_array`
I implemented the Default trait, but Rust does not seem to call this by default akin to a C++ constructor.
What is the proper way to initialize a fixed length array? I'd like to do an efficient in-place initialization rather than some sort of copy.
Related: Why is the Copy trait needed for default (struct valued) array initialization?
Related: Is there a way to not have to initialize arrays twice?
The safe but somewhat inefficient solution:
#[derive(Copy, Clone, Debug)]
struct Foo {
a: u32,
b: u32,
}
fn main() {
let mut foo_array = [Foo { a: 10, b: 10 }; 10];
}
Since you're specifically asking for a solution without copies:
use std::mem::MaybeUninit;
#[derive(Debug)]
struct Foo {
a: u32,
b: u32,
}
// We're just implementing Drop to prove there are no unnecessary copies.
impl Drop for Foo {
fn drop(&mut self) {
println!("Destructor running for a Foo");
}
}
pub fn main() {
let array = {
// Create an array of uninitialized values.
let mut array: [MaybeUninit<Foo>; 10] = unsafe { MaybeUninit::uninit().assume_init() };
for (i, element) in array.iter_mut().enumerate() {
let foo = Foo { a: i as u32, b: 0 };
*element = MaybeUninit::new(foo);
}
unsafe { std::mem::transmute::<_, [Foo; 10]>(array) }
};
for element in array.iter() {
println!("{:?}", element);
}
}
This is recommended by the documentation of MaybeUninit.
You can use the arrayvec crate:
Cargo.toml
[package]
name = "initialize_array"
version = "0.1.0"
edition = "2018"
[dependencies]
arrayvec = "0.7.2"
src/main.rs
use arrayvec::ArrayVec;
use std::iter;
#[derive(Clone)]
struct Foo {
a: u32,
b: u32,
}
fn main() {
let foo_array: [Foo; 10] = iter::repeat(Foo { a: 10, b: 10 })
.take(10)
.collect::<ArrayVec<_, 10>>()
.into_inner()
.unwrap_or_else(|_| unreachable!());
}
The easiest way is to derive Copy on your type and initialize the array with that, copying the element N times:
#[derive(Copy)]
struct Foo {
a: u32,
b: u32,
}
let mut foo_array = [Foo { a: 1, b: 2 }; 10];
If you want to avoid copying, there are a couple options. You can use the Default trait:
let mut foo_array: [Foo; 10] = Default::default();
However, this is limited to arrays up to 32 elements. With const generics, it is now possible for the standard library to provide Default for all arrays. However, this would be a backward incompatible change for subtle reasons that are being worked on.
For now, you can take advantage of the fact that const values are also allowed in array repetition expressions:
const FOO: Foo = Foo { a: 1, b: 2 };
let mut foo_array = [FOO; 10];
If you're on nightly, you can use array::map:
#![feature(array_map)]
let mut foo_array = [(); 10].map(|_| Foo::default())

How do I determine the size of an array at compile time in Rust?

I have a C library that expects string type that explicitly defines the string length:
#[repr(C)]
pub struct FFIStr {
len: usize,
data: *const u8,
}
Because this type is used as a static, I'd like a way to safely declare it using a const function or macro (instead of manually setting len).
My first attempt was to use a macro and len(), however in versions before 1.39.0, it is not possible to get the length of a slice as a const fn:
macro_rules! ffi_string {
($x:expr) => {
FFIStr { len: $x.len(), data: $x as *const u8 }
};
}
#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string!(b"Hello, world!");
error: core::slice::<impl [T]>::len` is not yet stable as a const function
My second attempt was to use std::mem::size_of<T>, but there doesn't appear to be a way to get the type of the static array short of using generics:
const fn ffi_string<T>(s: &'static T) -> FFIStr {
FFIStr { len: ::std::mem::size_of::<T>(), data: s as *const _ as *const _ }
}
#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string(b"Hello, world!");
While this works (surprisingly), it's horribly prone to misuse as it wildly casts whatever you pass it to a *const u8.
It seems like const_generics would be a nice solution to this, but they're currently unstable:
const fn ffi_string<const SIZE: usize>(s: &'static [u8; SIZE]) -> FFIStr {
FFIStr { len: SIZE, data: s as *const u8 }
}
#[no_mangle]
pub static mut X: FFIStr = ffi_string(b"Hello, world!");
error[E0658]: const generics are unstable
Is there a better way of determining the size of a static array at compile time?
In Rust 1.39.0 [T]::len was stabilised as a const function, now making this straight forward:
const ARRAY: [i32; 3] = [1, 2, 3];
const ARRAY_SIZE: usize = ARRAY.len();
fn main() {
assert_eq!(3, ARRAY_SIZE);
}
In earlier versions of Rust, here's one way based on the common C ARRAY_SIZE macro:
macro_rules! array_size {
($x:expr) => (
(size_of_val($x) / size_of_val(&$x[0]))
)
}
const fn size_of_val<T>(_: &T) -> usize {
std::mem::size_of::<T>()
}
fn main() {
assert_eq!(3, array_size!(&[1, 2, 3]));
assert_eq!(13, array_size!(b"Hello, world!"));
}
It uses a const generic function size_of_val<T> to determine the type and thus the size of a value passed by reference (the built-in std::mem::size_of_val isn't const).
Note: This doesn't work for arrays of size 0. This can be fixed by using size_of_val($x) / size_of_val(unsafe { &*$x.as_ptr() }) at the cost of wrongly accepting non-array types (e.g. &String).

How to extract a usize bytes and return them? [duplicate]

I want to create array like this:
let arr = [0; length];
Where length is a usize. But I get this error
E0307
The length of an array is part of its type. For this reason, this length
must be a compile-time constant.
Is it possible to create array with dynamic length? I want an array, not a Vec.
Is it possible to create array with dynamic length?
No. By definition, arrays have a length defined at compile time. A variable (because it can vary) is not known at compile time. The compiler would not know how much space to allocate on the stack to provide storage for the array.
You will need to use a Vec:
let arr = vec![0; length];
See also:
Is it possible to control the size of an array using the type parameter of a generic?
This should be possible after variable length arrays (VLA) are implemented.
You can create your own HeapArray. It's not that complicated if you read alloc's docs:
use std::alloc::{alloc, dealloc, Layout};
pub struct HeapArray<T> {
ptr: *mut T,
len: usize,
}
impl<T> HeapArray<T> {
pub fn new(len: usize) -> Self {
let ptr = unsafe {
let layout = Layout::from_size_align_unchecked(len, std::mem::size_of::<T>());
alloc(layout) as *mut T
};
Self { ptr, len }
}
pub fn get(&self, idx: usize) -> Option<&T> {
if idx < self.len {
unsafe { Some(&*(self.ptr.add(idx))) }
} else {
None
}
}
pub fn get_mut(&self, idx: usize) -> Option<&mut T> {
if idx < self.len {
unsafe { Some(&mut *(self.ptr.add(idx))) }
} else {
None
}
}
pub fn len(&self) -> usize {
self.len
}
}
impl<T> Drop for HeapArray<T> {
fn drop(&mut self) {
unsafe {
dealloc(
self.ptr as *mut u8,
Layout::from_size_align_unchecked(self.len, std::mem::size_of::<T>()),
)
};
}
}
impl<T> std::ops::Index<usize> for HeapArray<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
impl<T> std::ops::IndexMut<usize> for HeapArray<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.get_mut(index).unwrap()
}
}
You may also add methods like as_slice, get_unchecked, etc.

Initialise struct field as temporary directory and read from directory

I am trying to have a struct which has a field which I assume should be of type Result<TempDir>. When I initialise an implementation of the field with new(), I would like that particular field to be initialised by the creation of a new temp directory. Later, I want to implement a method to read from that directory.
Here's the code, I am more worried about the syntax and proper use of libraries (why exactly are there over four libraries for read/write buffering in Rust, this is insane) as the logic should be right. Dont worry too much about the trait implementations, I just need directions in the syntax. Please don't be too harsh, as I know it doesn't compile, but with just two changes it should.
extern crate rustc_back;
use std::path::Path;
use std::fs::File;
use rustc_back::tempdir::TempDir as TempDir;
pub struct MyStorage {
temp_dir : Result<TempDir>
}
impl MyStorage {
pub fn new() -> MyStorage {
//tempo = match TempDir::new("encrypt_storage");
let store = match TempDir::new("encrypt_storage") {
Ok(dir) => dir,
Err(e) => panic!("couldn't create temporary directory: {}", e)
};
MyStorage { temp_dir: store }
//MyStorage { temp_dir: TempDir::new("encrypt_storage") }
}
}
impl Storage for MyStorage {
fn get(&self, name: Vec<u8>) -> Vec<u8> {
//let mut f = std::fs::File::open(self.temp_dir.path() / name);
let mut f = std::fs::File::open(&self.temp_dir){
// The `desc` field of `IoError` is a string that describes the error
Err(why) => panic!("couldn't open: {}", why.description()),
Ok(file) => file,
};
let mut s = String::new();
//f.read_to_string(&mut s);
match f.read_to_string(&mut s){
Err(why) => panic!("couldn't read: {}", why.description()),
Ok(_) => print!("contains:\n{}", s),
}
s.to_vec()
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
// self.entries.push(Entry { name : name, data : data })
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
// self.entries.push(Entry { name : name, data : data })
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
}
After fixing the indentation (Rust uses 4 spaces per level), removing the Storage for since you didn't provide that trait, removing commented-out code, and adding a main, you are left with this:
extern crate rustc_back;
use std::path::Path;
use std::fs::File;
use rustc_back::tempdir::TempDir as TempDir;
pub struct MyStorage {
temp_dir : Result<TempDir>
}
impl MyStorage {
pub fn new() -> MyStorage {
let store = match TempDir::new("encrypt_storage") {
Ok(dir) => dir,
Err(e) => panic!("couldn't create temporary directory: {}", e)
};
MyStorage { temp_dir: store }
}
}
impl MyStorage {
fn get(&self, name: Vec<u8>) -> Vec<u8> {
let mut f = std::fs::File::open(self.temp_dir.path() / name);
let mut s = String::new();
f.read_to_string(&mut s);
s.to_vec()
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
}
fn main() {}
Compiling that has this error:
error: wrong number of type arguments: expected 2, found 1 [E0243]
temp_dir : Result<TempDir>
^~~~~~~~~~~~~~~
Which nicely points to the problematic type. Let's look at the docs for Result, which includes the definition:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
So Result has two type parameters - T is used for the success case, and E is used for the failure case. Your code is only specifying one of them. My guess is that you looked at the docs for TempDir and copy-and-pasted:
fn new(prefix: &str) -> Result<TempDir>
However, if you click on the Result there, you'll see it goes to io::Result, which is simply a type alias that binds E to io::Error:
type Result<T> = Result<T, Error>;
With all that exposition out of the way, you can "fix" your problem by changing your MyStorage struct:
pub struct MyStorage {
temp_dir: std::io::Result<TempDir>,
}
And then you will get another compiler error, as you are already dealing with the Result via the match in MyStorage::new. You aren't storing a io::Result<TempDir>, you are just storing a TempDir! Changing your struct further:
pub struct MyStorage {
temp_dir: TempDir,
}
unlocks a whole new set of errors for you to figure out; but now you have gotten past that first hurdle!

Calling trait static method from another static method (rust)

Can you call a trait static method implemented by types from another trait static method implemented in the trait? For example:
trait SqlTable {
fn table_name() -> String;
fn load(id: i32) -> Something {
...
Self::table_name() // <-- this is not right
...
}
}
This is now working thanks to Chris and Arjan (see comments/answers below)
fn main() {
let kiwibank = SqlTable::get_description(15,None::<Account>);
}
trait SqlTable {
fn table_name(_: Option<Self>) -> String;
fn get_description(id: i32, _: Option<Self>) -> String {
println!("Fetching from {} table", SqlTable::table_name(None::<Self>) );
String::from_str("dummy result")
}
}
struct Account {
id: i32,
name: String,
}
impl SqlTable for Account {
fn table_name(_: Option<Account>) -> String { String::from_str("account") }
}
You have to change Self to SqlTable:
trait SqlTable {
fn table_name() -> String;
fn load(id: i32) -> Self {
...
SqlTable::table_name() // <-- this is not right
...
}
}
Static methods are always called on a trait like SomeTrait::some_method(). Bug #6894 covers this issue.
Yes, you can call a trait static method [implemented by types] from another trait static method [implemented in the trait].
Static methods are always called on a trait like SomeTrait::some_method().
Where there is no Self or self in [a trait] function signature, it is not callable at present. The standard workaround until UFCS comes is to take an argument _: Option<Self> and pass it None::<T>.
See original question for code that (as of today) compiles.
This works without any workarounds with current Rust (verified with 1.65.0):
trait Trait {
fn x() -> i32 {
1
}
}
struct Struct {
y: i32,
}
impl Struct {
fn new(y: i32) -> Self {
Self { y }
}
fn f(self) -> i32 {
Self::x() + self.y
}
}
impl Trait for Struct {}
fn main() {
assert_eq!(Struct::new(1).f(), 2);
}

Resources