I have two structs. App and Item.
What I want to achieve is to store an Item in the items vector of the App struct by passing a mutable reference to the Items constructor.
pub struct App<'a> {
items: Vec<&'a Item>
}
impl<'a> App<'a> {
pub fn new() -> App<'a> {
App { items: Vec::new() }
}
pub fn register_item(&mut self, item: &'a Item) {
self.items.push(item);
}
}
pub struct Item;
impl Item {
pub fn new(app: &mut App) -> Item {
let item = Item;
app.register_item(&item);
item
}
}
fn main() {
let mut app = App::new();
let item = Item::new(&mut app);;
}
The code thows the following error:
test.rs:8:28: 8:32 error: `item` does not live long enough
test.rs:8 app.register_item(&item);
Is there any way to do this?
While Rc might be correct for your use case, it's good to understand why you are getting the error you are. Please read Why can't I store a value and a reference to that value in the same struct? as it has a much more in-depth discussion about why your code cannot work as-is. A simplified explanation follows.
Let's look at your constructor:
fn new(app: &mut App) -> Item {
let item = Item;
app.register_item(&item);
item
}
Here, we create a new Item on the stack at some address. Let's pretend that address is 0x1000. We then take the address of item (0x1000) and store it into the Vec inside the App. We then return item to the calling function, which resides in a different stack frame. That means that the address of item will change, which means that 0x1000 is no longer guaranteed to point to a valid Item! This is how Rust prevents you from making whole classes of memory errors!
I'd say that you'd normally see this written as:
fn main() {
let item = Item;
let mut app = App::new();
app.register_item(&item);
}
This will have the same problem if you tried to return app or item from this function, as the address will change.
If you have a straight-forward tree structure, I'd advocate for simply letting the parent nodes own the children:
struct App {
items: Vec<Item>
}
impl App {
fn new() -> App {
App { items: Vec::new() }
}
fn register_item(&mut self, item: Item) {
self.items.push(item);
}
}
pub struct Item;
fn main() {
let mut app = App::new();
app.register_item(Item);
}
Simple solution is to use Rc.
use std::rc::Rc;
pub struct Item;
impl Item {
pub fn new(app: &mut App) -> Rc<Item> {
let item = Rc::new(Item);
app.register_item(item.clone());
item
}
}
pub struct App {
items: Vec<Rc<Item>>
}
...
Related
Here's an example of a problem I ran into:
pub struct Item {
name: String,
value: LockableValue, // another struct that I'd like to mutate
}
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn value_mut(&mut self) -> &mut LockableValue {
&self.value
}
}
pub fn update(item: &mut Item) {
let value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", item.name());
}
Now, I know why this fails. I have a mutable reference to item through the mutable reference to the value.
If I convert the reference to an owned String, it works fine, but looks strange to me:
pub fn update(item: &mut Item) {
let name = { item.name().to_owned() };
let value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", name); // It works!
}
If I let value reference drop, then everything is fine.
pub fn update(item: &mut Item) {
{
let value = item.value_mut();
value.change(); // how it changes is unimportant
}
println!("Updated item: {}", item.name()); // It works!
}
The value.change() block is rather large, and accessing other fields in item might be helpful. So while I do have solutions to this issue, I'm wondering if there is a better (code-smell) way to do this. Any suggestions?
My intention behind the above structs was to allow Items to change values, but the name should be immutable. LockableValue is an tool to interface with another memory system, and copying/cloning the struct is not a good idea, as the memory is managed there. (I implement Drop on LockableValue to clean up.)
I was hoping it would be straight-forward to protect members of the struct from modification (even if it were immutable) like this... and I can, but it ends up looking weird to me. Maybe I just need to get used to it?
You could use interior mutability on only the part that you want to mutate by using a RefCell like ths:
use std::cell::{RefCell, RefMut};
pub struct LockableValue;
impl LockableValue {
fn change(&mut self) {}
}
pub struct Item {
name: String,
value: RefCell<LockableValue>, // another struct that I'd like to mutate
}
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn value_mut(&self) -> RefMut<'_, LockableValue> {
self.value.borrow_mut()
}
}
pub fn update(item: &Item) {
let name = item.name();
let mut value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", name);
}
That way you only need a shared reference to Item and you don't run into an issue with the borrow checker.
Not that this forces the borrow checks on value to be done at runtime though and thus comes with a performance hit.
I am currently struggling to find a way to mutate a value of a LookupMap inplace. Here is a small example that shows what I am trying to achieve.
// Aux structure
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Record {
pub i: usize,
}
impl Record {
pub fn inc(&mut self) {
self.i += 1;
}
}
// Main Contract
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Welcome {
pub records: LookupMap<String, Record>,
}
impl Default for Welcome {
fn default() -> Self {
Self {
records: LookupMap::new(b"a".to_vec()),
}
}
}
#[near_bindgen]
impl Welcome {
pub fn add_element(&mut self, key: &str) {
self.records.insert(&key.to_string(), &Record{i: 0});
}
pub fn inc_element(&mut self, key: &str) {
self.records.get(&key.to_string()).unwrap().inc();
}
}
When I run the following test, it fails:
#[test]
fn test_inc() {
// This is the function from the examples.
let context = get_context(vec![], false);
testing_env!(context);
let mut contract = Welcome::default();
let key1 = "k";
contract.add_element(key1);
// This test passes
assert_eq!(contract.records.get(&key1.to_string()).unwrap().i, 0);
// This should increment the value of the record in place
contract.records.get(&key1.to_string()).unwrap().inc();
// This test FAILS!
assert_eq!(contract.records.get(&key1.to_string()).unwrap().i, 1);
}
I am struggling to understand how can I get a mutable reference, in order to change the Record that is stored as a value on the LookupMap in place. With an Hashmap, I can use the get_mut(key) method, but here it does not seem to be the case.
Not sure if I missed anything on the docs, as I have been looking into then for a while.
Thank you!
There might be a better way to do this but the following change makes the test pass, replace:
contract.records.get(&key1.to_string()).unwrap().inc();
with:
let mut record = contract.records.get(&key1.to_string()).unwrap_or_else(|| env::panic(b"Record not found."));
record.inc();
contract.records.insert(&key1.to_string(), &record);
I believe that because LookupMap stores its data on the NEAR blockchain you have go through the insert method to ensure that the data is persisted.
I've been struggling with trying to understand how to properly annotate lifetimes in my application. I've simplified the actual code I had to this:
struct Item<'a> {
rf: &'a i32,
}
impl<'a> Item<'a> {
fn new(main: &'a App) -> Item<'a> {
Item{
rf: &main.i
}
}
}
struct App<'a> {
i: i32,
vec: Vec<Item<'a>>
}
impl<'a> App<'a> {
fn new() -> App<'a> {
App {
i: 32,
vec: vec![]
}
}
fn init(&mut self) {
self.vec.push(Item::new(self))
}
fn update(&self) {
for item in self.vec.iter() {
println!("{}", item.rf)
}
}
}
fn main() {
let app = App::new();
app.init();
app.update();
}
So there's a vector of items that hold a reference to something in App, which I know would exist as long as the app is alive, but the borrow-checker doesn't accept this. Could someone explain what's wrong with this code and how I could fix it?
You can find this code in rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ee8980c9e1b2a0622548525dbcf9f50f
I think the problem lays with how rust handles references and infers them. Here's a possible solution.
In short, we tell the rust compiler that in the App struct the i member lives for 'a length. This member we can then share with another struct that lives for at least 'a. we achieve this by telling the Item struct when creating it, that it at least has to live for at least the lifetime of the App struct. Because the compiler can be a bit picky about inferring and anonymous lifetimes we have to be explicit. We do this by adding a 'b to the new method of the Item struct. When we then call this method with the lifetime 'a of the app struct, the compiler knows that Item lives for as long as App. Sorry for the short explanation cause more than I probably know goes on here.
struct Item<'a> {
rf: &'a i32,
}
impl<'a> Item<'a> {
fn new<'b>(main: &'b App<'a>) -> Item<'a> {
Item{
rf: main.i
}
}
}
struct App<'a> {
i: &'a i32,
vec: Vec<Item<'a>>
}
impl<'a> App<'a> {
fn new() -> App<'a> {
App {
i: &32,
vec: vec![]
}
}
fn init(&mut self) {
let item = Item::new(self);
self.vec.push(item)
}
fn update(&self) {
for item in self.vec.iter() {
println!("{}", item.rf)
}
}
}
fn main() {
let mut app = App::new();
app.init();
app.update();
}
I'm making a functional builder that configures an object such as:
struct Person
{
name: String,
position: String
}
The builder itself keeps a list of boxed closures to be applied to the object when it needs to be constructed:
struct FunctionalBuilder<TSubject>
where TSubject : Default
{
actions: Vec<Box<dyn Fn(&mut TSubject) -> ()>>
}
impl<TSubject> FunctionalBuilder<TSubject>
where TSubject : Default
{
fn build(self) -> TSubject
{
let mut subj = TSubject::default();
for action in self.actions
{
(*action)(&mut subj);
}
subj
}
}
The idea being that one can aggregate this builder and then customize it for an object such as Person. Now, let's say I want to have a builder method called() that takes a name and saves the assignment of the name in the closure. I implement it as follows:
impl PersonBuilder
{
pub fn called(mut self, name: &str) -> PersonBuilder
{
let value = name.to_string();
self.builder.actions.push(Box::new(move |x| {
x.name = value.clone();
}));
self
}
}
Is this the right way of doing things? Is there a better way that avoids the temporary variable and clone() call?
Complete working example:
#[derive(Debug, Default)]
struct Person {
name: String,
position: String,
}
struct FunctionalBuilder<TSubject>
where
TSubject: Default,
{
actions: Vec<Box<dyn Fn(&mut TSubject) -> ()>>,
}
impl<TSubject> FunctionalBuilder<TSubject>
where
TSubject: Default,
{
fn build(self) -> TSubject {
let mut subj = TSubject::default();
for action in self.actions {
(*action)(&mut subj);
}
subj
}
fn new() -> FunctionalBuilder<TSubject> {
Self {
actions: Vec::new(),
}
}
}
struct PersonBuilder {
builder: FunctionalBuilder<Person>,
}
impl PersonBuilder {
pub fn new() -> Self {
PersonBuilder {
builder: FunctionalBuilder::<Person>::new(),
}
}
pub fn called(mut self, name: &str) -> PersonBuilder {
let value = name.to_string();
self.builder.actions.push(Box::new(move |x| {
x.name = value;
}));
self
}
pub fn build(self) -> Person {
self.builder.build()
}
}
pub fn main() {
let builder = PersonBuilder::new();
let me = builder.called("Dmitri").build();
println!("{:?}", me);
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=27eb6283836a478d5c68aa025aa4698d
You already do it, value is owned by your closure, the problem is that you require the Fn trait. This mean that action (the function) need to be able to be called many times. This mean value need to be cloned to keep it valid inside the closure. The closure can't give away its ownership.
One way would be to use FnOnce, which would make it possible to remove the clone, but this means that the builder can only be used once. To do this, use actions: Vec<Box<dyn FnOnce(&mut TSubject) -> ()>>, and action(&mut subj);.
More:
"cannot move a value of type FnOnce" when moving a boxed function
Fn* closure traits implemented for Box<dyn Fn*>
Basically a object (struct) is constructed by composing different components. Each concrete component being easily swapped by another component matching the interface (I guess trait).
I'm currently trying to implement with traits which got me into some errors and made me start thinking if this is a common thing in Rust.
// usage example
fn main() {
let obj = MainObject::new(Component1::new(), Component2::new(), Component3());
// Where each component is a type(struct) with some well predefined methods.
}
The main idea behind this is to implement the Component pattern commonly used in games. Basically the game would contain a lot of different objects, with slight variations in behavior and contained data. Instead of having a big class hierarchy, the objects are composed of standard components, more complete example would be.
pub struct Container
{
input: InputHandlerComponent, // Probably a trait
physics: PhysicsComponent, // Probably a trait
renderer: RendererCompoent // Probably a trait
}
impl Container {
fn new(p: PhysicsComponent, i: InputComponent, r: RenderComponent) -> Container {
Container {input: i, physics: p, renderer: r}
}
}
struct ConcretePhysicsComponent;
impl PhysicsComponent for ConcretePhysicsComponent
{
// ...
}
struct ConcreteInputComponent;
impl InputComponent for ConcreteInputComponent
{
// ...
}
struct ConcreteRendererComponent;
impl RendererComponent for ConcreteRendererComponent
{
// ...
}
struct AnotherConcreteRendererComponent;
impl RendererComponent for AnotherConcreteRendererComponent
{
// ...
}
// usage example
fn main() {
let obj = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), ConcreteRendererComponent::new());
// Where each component is a type(struct) with some well predefined methods.
// This is a slightly modified version of this object, with changed rendering behaviour
let obj2 = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), AnotherConcreteRendererComponent::new()); }
It sounds like you are just asking about traits, multiple concrete implementations of that trait, and a wrapper object that restricts itself to types that implement that trait. Optionally, the container can implement the trait by delegating it to the inner object.
trait Health {
fn life(&self) -> u8;
fn hit_for(&mut self, lost_life: u8);
}
#[derive(Debug, Copy, Clone)]
struct WimpyHealth(u8);
impl Health for WimpyHealth {
fn life(&self) -> u8 { self.0 }
fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life * 2; }
}
#[derive(Debug, Copy, Clone)]
struct BuffHealth(u8);
impl Health for BuffHealth {
fn life(&self) -> u8 { self.0 }
fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life / 2; }
}
#[derive(Debug, Copy, Clone)]
struct Player<H> {
health: H,
}
impl<H> Health for Player<H>
where H: Health
{
fn life(&self) -> u8 { self.health.life() }
fn hit_for(&mut self, lost_life: u8) { self.health.hit_for(lost_life) }
}
fn main() {
let mut player_one = Player { health: WimpyHealth(128) };
let mut player_two = Player { health: BuffHealth(128) };
player_one.hit_for(12);
player_two.hit_for(12);
println!("{:?}", player_one);
println!("{:?}", player_two);
}
it is not possible to have an array of such Players without using Boxed values
That's correct. An array or vector (or any generic type, really) needs to all be of the same type. This is especially important for arrays/vectors because their memory layout is contiguous and each item needs to be at a fixed interval.
If you were allowed to have different types, then you could have one player that had a health that took 1 byte and another player with health that took 2 bytes. Then all the offsets would be incorrect.
You can implement the Health trait for a Box<Health>, and then the Player objects can be stored sequentially, but they would each have a pointer to the appropriate concrete implementation of Health via the box.
impl<H: ?Sized> Health for Box<H>
where H: Health
{
fn life(&self) -> u8 { (**self).life() }
fn hit_for(&mut self, lost_life: u8) { (**self).hit_for(lost_life) }
}
fn main() {
let mut players = vec![
Player { health: Box::new(WimpyHealth(128)) as Box<Health> },
Player { health: Box::new(BuffHealth(128)) as Box<Health> }
];
for player in players.iter_mut() {
player.hit_for(42);
}
println!("{:?}", players[0].life());
println!("{:?}", players[1].life());
}