0

Definition:

 mapping(address => data)         clusterContract;

 struct data { //defined inside a Library.
    mapping(address => Library.Struct[]) my_status;
 }
 data list;     
 clusterContract[id] = list;

Usage:

clusterContract[msg.sender].my_status[id].push( Library.Struct({ status: status_ }));
delete clusterContract[msg.sender].my_status; //<=error occurs.

Error occurs:

Error: Unary operator delete cannot be applied to type 
mapping(address => struct ReceiptLib.Status storage ref[] storage ref)
    E               delete clusterContract[msg.sender].my_status;

On the other hand: delete clusterContract[msg.sender].my_status[id] works.

=> Does delete clusterContract[msg.sender] removes(initialise to 0) complete array of my_status as well?

[Q] How could I apply delete to complete storage ref[]? or do I required to iterate ref[](keep track of the ids) and apply delete one by one as shown on the example?

Example:

for(int i=0;i<storedIDs.size();i++)
   delete clusterContract[msg.sender].my_status[storedIDs[i]];

Thank you for your valuable time and help.

2

Deleting data does free up state space on clients using State Tree Pruning and should be done where possible when ever it is no longer required.

However mappings cannot simply be deleted in a single operation as the contract itself doesn't know where the data resides in the state address. For that it requires the key to be provided in order to delete the individually mapped elements.

Unbounded arrays, such as uint[] bytes etc, can be deleted in the one solidity operation however, the EVM must iteratively delete each slot individually. Care must be taken not to let such arrays grow too big because a future bulk delete operation may exhaust the block gas limit and brick your contract. The lesson here being that your garbage collection should also be designed to be atomic.

Storing new data is at a cost of 20000 per slot while overwriting data costs only 5000. Deleting data also costs 5000 per slot but returns a 15000 gas per slot refund at the end of your call.

What Rob has suggested is good practice with arrays because you can reuse reinitialised storage simply by setting length to 0. However you aren't using arrays but mappings which is a whole other ball game.

So, from your code fragments...

struct data {
    //defined inside a Library.
    mapping(address => Library.Struct[]) my_status;
}

mapping(address => data) clusterContract;
data list;     
clusterContract[id] = list;

...

clusterContract[msg.sender].my_status[id].push( Library.Struct({ status: status_ }));

when you try:

delete clusterContract[msg.sender].my_status; //<=error occurs.

you are trying to delete an entire mapping in one go because .mystatus is defined as:

mapping(address => Library.Struct[]) my_status;

but a mapping itself can't be deleted, only it's elements.

When I work with mappings of structs in particular, I write deconstructors to clean them up. So something like:

function destroy(id) internal

{
   delete clusterContract[msg.sender].my_status[storedIDs[id]]; 
}

But again try to design toward atomic operations with known life cycles rather than having to rely on expensive and messy bulk cleanup.

1

Delete doesn't do very much to the underlying data because this is an append-only storage system. No matter what we do, the data is still there in a previous world state.

Since you want to remove all elements in the array in one move:

pragma solidity ^0.4.6;

contract NullifyArray {

    address[] public status;

    function pushArray(address data)
        public
        returns(uint arrayLength)
    {
        return status.push(data);
    }

    function getArrayLength() 
        public 
        constant
        returns (uint arrayLength)
    {
        return status.length;
    }

    function nullifyArray()
        public
        returns(uint arrayLength)
    {
        status.length=0;
        return status.length;
    }

}

This will leave the basic structure in place, i.e. the struct still has an array, but it's length is 0 and no elements can be accessed by the public getter. It did nothing to erase the data that exists on the chain but it's logically gone.

If you need to be selective about which array rows to remove, I might suggest the Mapped Structs with Delete-enabled Index pattern here: Are there well-solved and simple storage patterns for Solidity?

Hope it helps.

  • Smart and this solution will use minimum gas. So data never be deleted, but logically we can put conditions for clients not to access the data. So for example when I getArrayValue() if status.length is 0 (even it was 100 before delete is done), I can do throw for the user who want to access the data? The thing is there could be delete operation and via same id it can be re-created. @Rob Hitchens – alper Apr 7 '17 at 7:39
  • Please note that I am looking for solution to complete remove Array. But definitely I will look into the link you shared. @Rob Hitchens – alper Apr 7 '17 at 7:57
  • 1
    On the read-back side, if array.length==0 and a function tries to access array[0], the EVM will throw for you. – Rob Hitchens Apr 7 '17 at 8:01
  • So basically status.length = 0 do the job. – alper Apr 7 '17 at 8:19
  • 1
    @Rob Hitchens Deleting data does free up space on clients using State Tree Pruning and should be done when ever possible to reclaim disk space. – o0ragman0o Apr 7 '17 at 11:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.