Initialize data structure's field in Rust with another data structure - data-structures

I have recently started to program in Rust and would like now to create a structure, which fields I would like to assign using other structures. In the code base, which I am working with, there is the following example:
pub struct StType1 {
f1: String,
f2: bool,
f3: usize,
}
pub struct StType2 {
// ...
field1: StType1,
// ...
}
impl StType2 {
// ...
pub fn build(mut self) {
// ...
let st = StType3 {
field1: self.field1,
};
// ...
}
pub fn field1(&self) -> &StType1 {
&self.field1
}
}
struct StType3 {
field1: StType1,
// ...
}
StType1 is the type of the field, which is contained in both structures StType2 and StType3. StType2 while running build() function creates StType3 and they should have the same value of field1.
In this code there is no move and seems no copy in this assignment field1: self.field1;. Why? What happens then?
I am asking because when I try to create a similar to StType3 - ObjType - structure outside this file:
let object1 = StType2::new(...);
let object2 = ObjType {
field1: object1.field1().clone(),
}
within my functions, I get a reference to StType1 after clone(), not an object, and when I am doing this:
let object1 = StType2::new(...);
let object2 = ObjType {
field1: *object1.field1().clone(),
}
I get an error Can't move:
move occurs because value has type
StType1, which does
not implement the Copy trait
I solve it by adding to the StType1 #[derive(Clone)]. But it worked before with StType2 without #[derive(Clone)]. Why should I add it now? Why there was no move happenning (or no clone required) for field1 when this StType3 was created inside StType2 and the field1 was initialized?

In this code there is no move and seems no copy in this assignment field1: self.field1. Why? What happens then?
This is incorrect – there is a move here, because Rust moves by default.
And this is possible because in the declaration above:
pub fn build(mut self) {
there is no & before the self, and so this build method takes self by-move.
In contrast, the method below:
pub fn field1(&self) -> &StType1 {
&self.field1
}
takes &self, i.e. a reference, and returns &StType1, another reference. So to get a StType1 to construct your object, you need to make it owned by calling .clone(). But that will only work if StType1 implements Clone, which is why you had to add #[derive(Clone)] to it.
Alternatively, you can change the field1() method to take self by-move as well:
pub fn field1(self) -> StType1 {
self.field1
}
The disadvantage of this approach is that since it consumes self, i.e. the entire struct, you won't be able to access any of the other fields after it's called.

Related

Rust macro generate enum from another enum

I am trying to solve a problem with code duplication to make it less error-prone.
I'm working with different Data Sources each with some specific properties. Here is how my data model looks like:
mod sources {
pub struct Ds1;
pub struct Ds2;
//hundreds more
pub trait DsTypeTrait{
type Input;
type Output;
}
impl DsTypeTrait for Ds1 {
type Input = u32;
type Output = u32;
}
impl DsTypeTrait for Ds2 {
type Input = String;
type Output = String;
}
//etc...
enum DataSource{
Ds1(Ds1),
Ds2(Ds2),
//...
}
}
So any time somebody wants to add support for a new data source they need to add it to the enum DataSource. The PROBLEM with the solution is that if another module contains some custom data format for, e.g. DataSource communications it would be required to add the DataSource in the 2 places which is extremely error-prone. For example:
mod some_other {
use super::sources::*;
struct DataSourceDataInputOutput<Ds: DsTypeTrait>{
input: <Ds as DsTypeTrait>::Input,
output: <Ds as DsTypeTrait>::Output
}
enum DataSourceIO{
Ds1(DataSourceDataInputOutput<Ds1>),
Ds2(DataSourceDataInputOutput<Ds2>)
//Extremely easy to forget to add here
}
}
QUESTION: Given the enum DataSource is it possible to write a macro to generate enum DataSourceIO automatically and avoid modifying enum DataSourceIO manually each time a new DataSource is added?
It seems enum parsing is a complicated issue so it is reasonable to move the DataSource actual declaration in the macro itself. Here is what I came up with:
#[macro_use]
mod sources {
macro_rules! data_sources {
($name:ident, $type:tt) => {
enum $name{
Ds1($type<Ds1>),
Ds2($type<Ds2>)
}
}
}
//...
}
mod some_other {
struct DataSourceDataInputOutput<Ds: DsTypeTrait>{
input: <Ds as DsTypeTrait>::Input,
output: <Ds as DsTypeTrait>::Output
}
data_sources!(Test, DataSourceDataInputOutput);
fn test_fn() {
let ds1io: DataSourceDataInputOutput<Ds1> = DataSourceDataInputOutput{
input: 1,
output: 2
};
let test = DataSourceIO::Ds1(ds1io); //works ok
}
}

String attribute set in init method always returns empty string

I have the following struct with impl:
#[near_bindgen]
#[derive(Default, Serialize, Deserialize, BorshDeserialize, BorshSerialize, Debug)]
pub struct MyStruct {
owner: String
}
#[near_bindgen(init => new)]
impl MyStruct {
fn new() -> Self {
Self {
owner: "bob".to_string()
}
}
fn get_owner(&self) -> String {
return self.owner;
}
}
Then I deploy the contract using near deploy my_contract --masterAccount myAccount
If I call get_owner using near-shell: near call my_contract get_owner --accountId=myAccount It always returns "" instead of the expected "bob".
It seems like the new method might not get called on deployment.
Initializer doesn't automatically get called on deploy. deploy just deploys the code and doesn't call anything on the contract. We should probably add a new method to shell, that does deploy_and_call. But for now just call new manually.
The reason why we don't initialize automatically is that initializer might take additional arguments. You can pass an owner to new method. Here is an example how to use initializer with custom arguments and as well as how to make sure a contract can't be called without initialization:
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct FunToken {
/// AccountID -> Account details.
pub accounts: Map<AccountId, Account>,
/// Total supply of the all token.
pub total_supply: Balance,
}
impl Default for FunToken {
fn default() -> Self {
env::panic(b"Not initialized");
unreachable!();
}
}
#[near_bindgen(init => new)]
impl FunToken {
pub fn new(owner_id: AccountId, total_supply: Balance) -> Self {
let mut ft = Self { accounts: Map::new(b"a".to_vec()), total_supply };
let mut account = ft.get_account(&owner_id);
account.balance = total_supply;
ft.accounts.insert(&owner_id, &account);
ft
}
}
From here: https://github.com/nearprotocol/near-bindgen/blob/master/examples/fun-token/src/lib.rs#L52-L77
Basically it panics during Default call, so non initialized contract can't be called.
Initializer functions are usually used when you need to parametrize the initialization of the contract. If there are no parameters then just implement Default trait:
impl Default for MyStruct {
fn default() -> Self {
Self {
owner: "bob".to_string()
}
}}

Replace a struct member with a new value that uses the previous value

I have a struct which owns a boxed value of some trait type. The struct itself also implements the same trait. I would like to replace the value with a new instance of the same struct, which wraps it.
The following code, which does not compile, should make it more clear what I am trying to do:
trait T {}
struct S {
t: Box<dyn T>,
}
impl T for S {}
impl S {
fn new(t: Box<dyn T>) -> Self {
Self { t }
}
fn wrap_t(&mut self) {
self.t = Box::new(Self::new(self.t))
}
}
This fails:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:14:37
|
14 | self.t = Box::new(Self::new(self.t))
| ^^^^ cannot move out of borrowed content
Implementing wrap_t like this does compile:
use std::mem;
fn wrap_t(&mut self) {
unsafe {
let old_t = mem::replace(&mut self.t, mem::uninitialized());
let new_t = Box::new(Self::new(old_t));
let uninit = mem::replace(&mut self.t, new_t);
mem::forget(uninit);
}
}
I wonder if there is a safe way to do this.
The only unsafe function you are using is mem::uninitialized. You need something to pass to mem::replace, but implementing Default won't work because default() returns Self, which prevents it from being object-safe. Similarly, you can't implement Clone to duplicate the old value, since clone() also returns Self.
You can just implement a dummy type for the purpose though:
struct Dummy;
impl T for Dummy {}
fn wrap_t(&mut self) {
let old_t = mem::replace(&mut self.t, Box::new(Dummy));
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
You also won't need the mem::forget here now either (I'm assuming that was there to prevent undefined behaviour when the uninitialised memory was dropped).
As an alternative to Clone, you can roll your own own, which clones to a Box<dyn T>, avoiding having a Self in the method signature, so the trait stays object safe:
trait T: Debug {
fn clone_in_box(&self) -> Box<dyn T>;
}
impl T for S {
fn clone_in_box(&self) -> Box<dyn T> {
Box::new(S {
t: self.t.clone_in_box(),
})
}
}
fn wrap_t(&mut self) {
let cloned = self.clone_in_box();
let old_t = mem::replace(&mut self.t, cloned);
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
There is also an alternative design, which is much simpler to understand when reading the code. That is just to consume self and return a new object:
fn wrap_t(self) -> Self {
Self::new(Box::new(Self::new(self.t)))
}
And instead of this:
s.wrap_t();
You would do:
s = s.wrap_t();

Pass instance of type from argument list into function declaration (dynamic environment capture)

I made a library that implements an optimization algorithm here. These are the signatures for the relevant function and trait:
pub fn cmaes_loop<T>(object: &T, options: CMAESOptions) -> Option<Vec<f64>>
where T: 'static + FitnessFunction
pub trait FitnessFunction {
fn get_fitness(&self, parameters: &[f64]) -> f64;
}
If I apply the library to a simple problem, I can calculate the fitness based on the parameters given. However, let's say I want to optimize the shape of a wing for a given size and material:
struct Wing<'a> {
material: String,
size: i32,
parameters: &'a [f64]
}
I can implement the fitness function to read from the other fields and factor them into the calculation, then give an instance of Wing to the cmaes_loop function to optimize a specific material and size. I am making another library that will use this one. It optimizes something else (like Wing), so I have a wrapper trait to make the second library easier to use:
pub trait WingFitnessFunction {
fn get_fitness(&self, wing: &Wing) -> f64;
}
I implement the FitnessFunction trait like this:
impl FitnessFunction for Wing {
fn get_fitness(&self, parameters: &[f64]) -> f64 {
let wing = Wing {
parameters: parameters,
.. self
};
// error here
let result = WingFitnessFunction::get_fitness(wing);
}
}
WingFitnessFunction has a &self argument for the the same reason FitnessFunction does. I want a user of my second library to be able to pass an instance of their type that implements the WingFitnessFunction. I put the impl of FitnessFunction into the body of a function that takes an instance of the user's type:
fn foo<T: WingFitnessFunction>(object: T) {
impl FitnessFunction for Wing {
...
object.get_fitness(wing)
}
But when I try to call the get_fitness method of object I get this error:
can't capture dynamic environment in a fn item; use the || { ... } closure form instead
I need to know how to call object's get_fitness method on a specific instance (a closure could do this, but I do not know how I would make it work with the cmaes_loop function).
The solution to my problem is to add a wrapper:
struct WingWrapper<'a, T: WingFitnessFunction> {
wing: Wing,
object: T
}
And implement FitnessFunction for it:
impl<'a, T: WingFitnessFunction> FitnessFunction for WingWrapper<'a, T> {
fn get_fitness(&self, parameters: &[f64]) -> f64 {
let wing = Wing {
parameters: parameters,
.. self.wing
}
self.object.get_fitness(&wing)
}
}
In the function that takes a type that implements WingFitnessFunction as an argument, first build a WingWrapper:
let wrapper = WingWrapper {
wing: // Instance of wing here
object: // Instance of object here (taken as an argument)
}
Now, you can call the get_fitness method from the WingFitnessFunction trait through the method from FitnessFunction:
println!("{}", wrapper.get_fitness(&[1, 2, 3]));
This does not add any difficult to using the library; it is exactly the same from the user's perspective.

How can I force a struct's field to always be immutable in Rust?

In Rust, you don't specify mutability inside a struct, but it is inherited from the variable binding. That's great, but is it possible to force a field to be always immutable, even when the root is mutable?
Something like this hypothetical syntax:
struct A {
immut s: Shape, // immutable by design
bla: Bla, // this field inheriting (im)mutability
}
let mut a = make_a();
a.s = x/*...*/; // illegal
This would help to maintain nice semantic restrictions in a program, just like Java's final does (in a very limited way).
Also, we could imagine this kind of struct having some non-owning references to internal immutable data, taking advantage of this immutability...
It's impossible to have immutability of a single field. That was an option in an ancient version of Rust (think before 0.8), but it was dropped because the rules confused a LOT of people. How was it confusing, you might ask? Think about it like this: if a field is declared mutable and struct is declared mutable and the reference used was an immutable reference (&) then the field is _______.
The best, as Lily Ballard noted, is that you can declare your Shape field as private and make a getter method using impl A {...}.
mod inner {
pub struct A {
s: i32, // can't be seen outside of module
pub bla: i32,
}
impl A {
pub fn new() -> Self {
Self { s: 0, bla: 42 }
}
pub fn get_s(&self) -> i32 {
self.s
}
}
}
let mut a = inner::A::new();
a.s = 42; // illegal
println!("{}", a.s); // also illegal
println!("{}", a.get_s()); // could be made to serve as a read-only method
error[E0616]: field `s` of struct `main::inner::A` is private
--> src/main.rs:20:5
|
20 | a.s = 42; // illegal
| ^^^
error[E0616]: field `s` of struct `main::inner::A` is private
--> src/main.rs:21:20
|
21 | println!("{}", a.s); // also illegal
| ^^^
There is proposition that might drop notions of mutability and immutability completely (you can't say a struct never changes). See Niko's explanation for that change.
You can create a struct and only implement the Deref trait for it. Without the DerefMut trait it won't be possible for contained values to be mutated.
https://doc.rust-lang.org/std/ops/trait.Deref.html
This way the compiler will make the member usable as if it's not wrapped in another struct, no need for any written getter method call.
use std::ops::Deref;
/// A container for values that can only be deref'd immutably.
struct Immutable<T> {
value: T,
}
impl<T> Immutable<T> {
pub fn new(value: T) -> Self {
Immutable { value }
}
}
impl<T> Deref for Immutable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
struct Foo {
bar: Immutable<Vec<u8>>,
baz: usize,
}
impl Foo {
pub fn new(vec: Vec<u8>) -> Self {
Foo {
bar: Immutable::new(vec),
baz: 1337,
}
}
pub fn mutate(&mut self) {
self.bar.push(0); // This will cause a compiler error
}
}
|
| self.bar.push(0);
| ^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `runnable::immutable::Immutable<std::vec::Vec<u8>>`
You can't force immutability on a field. How would the struct mutate its own value when necessary?
What you can do is make the field private and expose a getter method to return a reference to it (or to copy/clone the value).
A Solution could be to have a more general approach:
pub struct Immutable<T> {
value : T ,
}
impl<T> Immutable<T> {
pub fn new(value : T) -> Immutable<T> {
Immutable { value : value }
}
pub fn get( &self) -> &T { &self.value }
}
Now it is possible to use the Immutable struct in every case for every other type.
Given this into a module avoids changing the content of the Immutable object. It is still possible to change the variable which holds the Immutable object itself by overwriting it by a new Object but you should notice it by the Immutable::new statement and so you can avoid using it.
In many cases an associated constant achieves the desired behaviour:
struct Foo { blah: Blah }
impl Foo {
const S: Shape = Shape { x: 1, y: 1 };
}
Of course, constants cannot be set on creation, they are set at compile-time. Making the field private as explained in other answers will work if dynamicism is required.
Probably the easiest way to achieve this today is by using the readonly crate from prolific developer David Tolnay (author of serde and many others). From the github documentation:
This crate provides an attribute macro to expose struct fields that are readable and writable from within the same module but readable only outside the module.
#[readonly::make]
pub struct S {
// This field can be read (but not written) by super.
#[readonly]
pub(super) readable: i32,
// This field can be neither read nor written by other modules.
private: i32,
}

Resources