How can I set only some struct members to their default values? - struct

I have struct B:
struct A {}
struct B {
a: A,
b: u32,
c: i8,
z: usize,
}
A does not have a Default implementation and no default that makes sense. The fields b..z all need to be initialised to the default (in this case, 0), and A will be initialised to some runtime value:
let b = B {
a: some_a(),
b: 0,
c: 0,
z: 0,
};
This is not ideal.
Even worse, if an aa were to be added to the struct, there would need to be another member added to the initialiser. Using ..Default::default() doesn't work because B doesn't have a Default implementation, because any As in the default will be too expensive to compute to simply throw away afterwards. Furthermore, I would need an extremely long struct initialiser in the Default implementation anyway.
Is there any way to set the remaining struct members to their default values (so that b would be set to the default of u32) without writing all the member names out?
Preferably, there should be minimum change required to struct initialisers when new members are added. This means this is not ideal:
let b = B {
a: some_a(),
b: Default::default(),
c: Default::default(),
z: Default::default(),
};

If you have large structs, you can make your life easier by deriving a builder or a constructor function. There are a few crates that do this, and a popular one is derive_builder.
Using that crate, you can do something like:
use derive_builder::Builder;
// this derive will generate a struct called BBuilder
#[derive(Builder)]
struct B {
a: A,
#[builder(default = "0")]
b: u32,
#[builder(default = "0")]
c: i8,
/* ... */
#[default(default = "0")]
z: usize
}
fn main() {
let a = A { ... };
// all default values for fields b..z
let b = BBuilder::default().a(a).build().unwrap();
// or specify just some of the fields
let b = BBuilder::default()
.a(a)
.c(42)
.z(255)
.build()
.unwrap();
}
Adding new fields to B will not impact code that uses BBuilder, as long as the fields have defaults. The downside is that you get a runtime panic if you miss a required field, rather than a compilation error.
Another crate is derive-new, which is a bit simpler. You would use it like this:
use derive_new::new;
#[derive(new)]
struct B {
a: A,
#[new(default)]
b: u32,
#[new(default)]
c: i8,
/* ... */
#[new(default)]
z: usize
}
fn main() {
let a = A { ... };
// all default values for fields b..z
let b = B::new(a);
// To specify some of the default fields, you need to mutate
let mut b = B::new(a);
b.c = 42;
b.z = 255;
}
This doesn't generate any extra structs, it just adds a new method, which takes all of the non-default arguments. It is a compilation error if you miss any non-default fields out.

Related

Rust private struct fields and defaults and destructuring?

Let's say I create a struct.
mod foostruct {
#[derive(Default)]
pub struct Foo {
a: u64,
pub b: u64,
}
}
Is it true that this struct, because of the visibility rules can not be created externally with a default,
Foo { b: 42, ..Default::default() }
And, also cannot have it's visible members destructed?
let Foo { b, ... } = foo;
I was just having a conversation with a friend and both of these were brought up, and I just never thought of this. I was previously always using builders, and hadn't considered destruction/defaults as a detriment to the pattern.
Both of these bits of code are sugar for something else, so we can reason about their characteristics by examining what they desugar to.
Foo { b: 42, ..Default::default() }
Desugars to roughly:
{
let temp: Foo = Default::default();
Foo { b: 42, a: temp.a }
}
This is disallowed, because Foo::a is not visible.
let Foo { b, .. } = foo;
Desugars to this (with an additional static type-check that foo is indeed a Foo):
let b = foo.b;
This is allowed because the private fields are never actually read.

How do I destructure an object without dropping it?

I have a struct that I want to take by value, mutate and then return. I want to also mutate its generic type as I use this state for statically ensuring correct order of function calls for making safe FFI (playground):
use core::marker::PhantomData;
struct State1 {}
struct State2 {}
struct Whatever {}
struct X<State> {
a: Whatever,
b: Whatever,
c: Whatever,
_d: PhantomData<State>,
}
impl<State> Drop for X<State> {
fn drop(&mut self) {}
}
fn f(x: X<State1>) -> X<State2> {
let X { a, b, c, _d } = x;
//mutate a, b and c
X {
a,
b,
c,
_d: PhantomData,
} // return new instance
}
Because X implements Drop, I get:
error[E0509]: cannot move out of type `X<State1>`, which implements the `Drop` trait
--> src/lib.rs:19:29
|
19 | let X { a, b, c, _d } = x;
| - - - ^ cannot move out of here
| | | |
| | | ...and here
| | ...and here
| data moved here
|
= note: move occurs because these variables have types that don't implement the `Copy` trait
I don't want to drop anything as I am not destroying x, just repackaging it. What is the idiomatic way to prevent dropping x?
Moving data out of the value would leave it in an undefined state. That means that when Drop::drop is automatically run by the compiler, you'd be creating undefined behavior.
Instead, we can use unsafe Rust to prevent automatic dropping of the value and then pull the fields out ourselves. Once we pull one field out via ptr::read, the original structure is only partially initialized, so I also use MaybeUninit:
fn f(x: X<State1>) -> X<State2> {
use std::{mem::MaybeUninit, ptr};
// We are going to uninitialize the value.
let x = MaybeUninit::new(x);
// Deliberately shadow the value so we can't even try to drop it.
let x = x.as_ptr();
// SAFETY[TODO]: Explain why it's safe for us to ignore the destructor.
// I copied this from Stack Overflow and didn't even change the comment!
unsafe {
let a = ptr::read(&(*x).a);
let b = ptr::read(&(*x).b);
X {
a,
b,
_s: PhantomData,
}
}
}
You do need to be careful that you get all of the fields out of x, otherwise you could cause a memory leak. However, since you are creating a new struct that needs the same fields, this is an unlikely failure mode in this case.
See also:
How to move one field out of a struct that implements Drop trait?
Can not move out of type which defines the `Drop` trait [E0509]
How can I move a value out of the argument to Drop::drop()?
Temporarily move out of borrowed content
The contract you've created with the compiler by implementing Drop is that you have code that must run when an X is destroyed, and that X must be complete to do so. Destructuring is antithetical to that contract.
You can use ManuallyDrop to avoid Drop being called, but that doesn't necessarily help you destructure it, you'll still have to pull the fields out yourself. You can use std::mem::replace or std::mem::swap to move them out leaving dummy values in their place.
let mut x = ManuallyDrop::new(x);
let mut a = std::mem::replace(&mut x.a, Whatever {});
let mut b = std::mem::replace(&mut x.b, Whatever {});
let mut c = std::mem::replace(&mut x.c, Whatever {});
// mutate a, b, c
X { a, b, c, _d: PhantomData }
Note: this will also prevent the dummy a, b, and c from being dropped as well; potentially causing problems or leaking memory depending on Whatever. So I'd actually advise against this and use Peter Hall's answer if unsafe is unsavory.
If you truly want the same behavior and avoid creating dummy values, you can use unsafe code via std::ptr::read to move the value out with the promise that the original won't be accessed.
let x = ManuallyDrop::new(x);
let mut a = unsafe { std::ptr::read(&x.a) };
let mut b = unsafe { std::ptr::read(&x.b) };
let mut c = unsafe { std::ptr::read(&x.c) };
drop(x); // ensure x is no longer used beyond this point
// mutate a, b, c
X { a, b, c, _d: PhantomData }
Another unsafe option would be to use std::mem::transmute to go directly from X<State1> to X<State2>.
let mut x: X<State2> = unsafe { std::mem::transmute(x) };
// mutate x.a, x.b, x.c
x
If the state type isn't actually used for the fields at all (meaning all Xs are truly identical), its probably safe given that you also decorate X with #[repr(C)] to ensure the compiler doesn't move fields around. But I may be missing some other guarantee, std::mem::transmute is very unsafe and easy to get wrong.
You can separate the state-tracking PhantomData from the droppable struct:
use core::marker::PhantomData;
struct State1 {}
struct State2 {}
struct Whatever {}
struct Inner {
a: Whatever,
b: Whatever,
c: Whatever,
}
struct X<State> {
i: Inner,
_d: PhantomData<State>,
}
impl Drop for Inner {
fn drop(&mut self) {}
}
fn f(x: X<State1>) -> X<State2> {
let X { i, _d } = x;
//mutate i.a, i.b and i.c
X {
i,
_d: PhantomData,
} // return new instance
}
This avoids unsafe and ensures that a, b and c are kept in a group and will be dropped together.
You can avoid unsafe code, as suggested in the other answers, by ensuring that each value is replaced with a value when you move it, so that x is never left in an invalid state.
If the field types implement Default you can use std::mem::take:
use std::mem;
fn f(mut x: X<State1>) -> X<State2> {
let mut a = mem::take(&mut x.a);
let mut b = mem::take(&mut x.b);
let mut c = mem::take(&mut x.c);
// mutate a, b and c
// ...
// return a new X
X { a, b, c, _d: PhantomData }
}
Now it is safe for x to be dropped because it contains valid values for each field. If the field types don't implement Default then you could instead use std::mem::swap to replace them with a suitable dummy value.

How can I move structs containing Vec to/from a static array without getting E0507?

I need a static array of structs and the structs contain a Vec. I can manage the lifetimes of the actual values. I get the following error:
: Mar23 ; cargo test
Compiling smalltalk v0.1.0 (/Users/dmason/git/AST-Smalltalk/rust)
error[E0507]: cannot move out of `dispatchTable[_]` as `dispatchTable` is a static item
--> src/minimal.rs:32:44
|
30 | let old = ManuallyDrop::into_inner(dispatchTable[pos]);
| ^^^^^^^^^^^^^^^^^^ move occurs because `dispatchTable[_]` has type `ManuallyDrop<Option<Box<Dispatch>>>`, which does not implement the `Copy` trait
error: aborting due to previous error
Here is a minimal compilable example:
#[derive(Copy, Clone)]
struct MethodMatch {
hash: i64,
method: Option<bool>,
}
#[derive(Clone)]
pub struct Dispatch {
class: i64,
table: Vec<MethodMatch>,
}
const max_classes : usize = 100;
use std::mem::ManuallyDrop;
const no_dispatch : ManuallyDrop<Option<Box<Dispatch>>> = ManuallyDrop::new(None);
static mut dispatchTable : [ManuallyDrop<Option<Box<Dispatch>>>;max_classes] = [no_dispatch;max_classes];
use std::sync::RwLock;
lazy_static! {
static ref dispatchFree : RwLock<usize> = {RwLock::new(0)};
}
pub fn addClass(c : i64, n : usize) {
let mut index = dispatchFree.write().unwrap();
let pos = *index;
*index += 1;
replaceDispatch(pos,c,n);
}
pub fn replaceDispatch(pos : usize, c : i64, n : usize) -> Option<Box<Dispatch>> {
let mut table = Vec::with_capacity(n);
table.resize(n,MethodMatch{hash:0,method:None});
unsafe {
let old = ManuallyDrop::into_inner(dispatchTable[pos]);
dispatchTable[pos]=ManuallyDrop::new(Some(Box::new(Dispatch{class:c,table:table})));
old
}
}
The idea I had was to have replaceDispatch create a new Dispatch option object, and replace the current value in the array with the new one, returning the original, with the idea that the caller will get the Dispatch option value and be able to use and then drop/deallocate the object.
I found that it will compile if I add .clone() right after the identified error point. But then the original value never gets dropped, so (the into_inner is redundant and) I'm creating a memory leak!. Do I have to manually drop it (if I could figure out how)? I thought that's what ManuallyDrop bought me. In theory, if I created a copy of the fields from the Vec into a copy, that would point to the old data, so when that object got dropped, the memory would get freed. But (a) that seems very dirty, (b) it's a bit of ugly, unnecessary code (I have to handle the Some/None cases, look inside the Vec, etc.), and (c) I can't see how I'd even do it!!!!
As the compiler tells you, you cannot move a value out of a place observable by others. But since you have the replacement at the ready, you can use std::mem::replace:
pub fn replaceDispatch(pos: usize, c: i64, n: usize) -> Option<Box<Dispatch>> {
... table handling omitted ...
unsafe {
let old = std::mem::replace(
&mut dispatchTable[pos],
ManuallyDrop::new(Some(Box::new(Dispatch {
class: c,
table: table,
}))),
);
ManuallyDrop::into_inner(old)
}
}
Playground
In fact, since you're using the Option to manage the lifetime of Dispatch, you don't need ManuallyDrop at all, and you also don't need the Box: playground.

Rust macro that counts and generates repetitive struct fields

I want to write a macro that generates varying structs from an integer argument. For example, make_struct!(3) might generate something like this:
pub struct MyStruct3 {
field_0: u32,
field_1: u32,
field_2: u32
}
What's the best way to transform that "3" literal into a number that I can use to generate code? Should I be using macro_rules! or a proc-macro?
You need a procedural attribute macro and quite a bit of pipework. An example implementation is on Github; bear in mind that it is pretty rough around the edges, but works pretty nicely to start with.
The aim is to have the following:
#[derivefields(u32, "field", 3)]
struct MyStruct {
foo: u32
}
transpile to:
struct MyStruct {
pub field_0: u32,
pub field_1: u32,
pub field_2: u32,
foo: u32
}
To do this, first, we're going to establish a couple of things. We're going to need a struct to easily store and retrieve our arguments:
struct MacroInput {
pub field_type: syn::Type,
pub field_name: String,
pub field_count: u64
}
The rest is pipework:
impl Parse for MacroInput {
fn parse(input: ParseStream) -> syn::Result<Self> {
let field_type = input.parse::<syn::Type>()?;
let _comma = input.parse::<syn::token::Comma>()?;
let field_name = input.parse::<syn::LitStr>()?;
let _comma = input.parse::<syn::token::Comma>()?;
let count = input.parse::<syn::LitInt>()?;
Ok(MacroInput {
field_type: field_type,
field_name: field_name.value(),
field_count: count.base10_parse().unwrap()
})
}
}
This defines syn::Parse on our struct and allows us to use syn::parse_macro_input!() to easily parse our arguments.
#[proc_macro_attribute]
pub fn derivefields(attr: TokenStream, item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(attr as MacroInput);
let mut found_struct = false; // We actually need a struct
item.into_iter().map(|r| {
match &r {
&proc_macro::TokenTree::Ident(ref ident) if ident.to_string() == "struct" => { // react on keyword "struct" so we don't randomly modify non-structs
found_struct = true;
r
},
&proc_macro::TokenTree::Group(ref group) if group.delimiter() == proc_macro::Delimiter::Brace && found_struct == true => { // Opening brackets for the struct
let mut stream = proc_macro::TokenStream::new();
stream.extend((0..input.field_count).fold(vec![], |mut state:Vec<proc_macro::TokenStream>, i| {
let field_name_str = format!("{}_{}", input.field_name, i);
let field_name = Ident::new(&field_name_str, Span::call_site());
let field_type = input.field_type.clone();
state.push(quote!(pub #field_name: #field_type,
).into());
state
}).into_iter());
stream.extend(group.stream());
proc_macro::TokenTree::Group(
proc_macro::Group::new(
proc_macro::Delimiter::Brace,
stream
)
)
}
_ => r
}
}).collect()
}
The behavior of the modifier creates a new TokenStream and adds our fields first. This is extremely important; assume that the struct provided is struct Foo { bar: u8 }; appending last would cause a parse error due to a missing ,. Prepending allows us to not have to care about this, since a trailing comma in a struct is not a parse error.
Once we have this TokenStream, we successively extend() it with the generated tokens from quote::quote!(); this allows us to not have to build the token fragments ourselves. One gotcha is that the field name needs to be converted to an Ident (it gets quoted otherwise, which isn't something we want).
We then return this modified TokenStream as a TokenTree::Group to signify that this is indeed a block delimited by brackets.
In doing so, we also solved a few problems:
Since structs without named members (pub struct Foo(u32) for example) never actually have an opening bracket, this macro is a no-op for this
It will no-op any item that isn't a struct
It will also no-op structs without a member

Access associated constant items without scope qualifier

I would like to use the const C inside the impl R6502 without having to specify the scope R6502::
use bit::BitIndex;
pub struct R6502 {
pub sr: u8, // status register
}
impl R6502 {
// status flag indexs
const C: usize = 0;
const Z: usize = 1;
pub fn step(&mut self) {
self.sr.set_bit(R6502::C, false); // this is what I have to do
self.sr.set_bit(C, false); // this is what I want to do
}
}
I tried use self::C and some other combinations of use to only get errors about items not found.
useing of associated constants is not implemented in Rust 1.20. I haven't found an issue for that, so you can create your own issue in Rust GitHub repository.
In the meantime you can use type alias to reduce character count.
type P = R6502;
self.sr.set_bit(P::C, false);

Resources