-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Require taking self as argument for contract methods that interact with self #96
Comments
I REALLY like I think it's worth exploring whether this can serve as re-entrancy protection as well. If you went the rust route and were able to enforce there only ever being one mutable handle to |
Yep, in I personally think that this is a very interesting idea. |
I think using Are there instances where reentrance is desirable? If so, how should we allow it in the language (assuming it's disabled by default)? Something else worth thinking about here is how storage pointers would play in with this. Say for example we have this code: contract Foo:
my_array: u256[100]
def bar(self, mut sto_array: Storage<u256[100]>):
sto_array[26] = 42
pub def baz(mut self):
self.bar(self.my_array) note: storage/memory modifiers have not been introduced to Fe yet, but it has been discussed. In the example above, In general, I'm hesitant to add storage and memory modifiers to Fe for reasons like this. It complicates compiler implementation and makes things more confusing for devs. I think that in Fe, mutating state should always happen in a statement that begins with a mutable reference to For now, I think the simplest thing we can do is require that all parameters be passed by value or memory pointer. So rewriting the initial example: contract Foo:
my_array: u256[100]
def bar(self, mut mem_array: u256[100]):
mem_array[26] = 42
pub def baz(self):
self.bar(self.my_array) # my_array is copied to memory This way, state is not modified within Further down the road, we could introduce more gas-efficient behavior based on mutability. For example, say we have this code: contract Foo:
my_array: u256[100]
def bar(self, some_array: u256[100]) -> u256:
return some_array[42]
pub def baz(self):
my_num: u256 = self.bar(self.my_array) # my_array is passed by storage pointer since its an immutable reference In cases where a storage value is being passed into some function as an immutable parameter, we wouldn't need to copy it because we know that that storage value will never be modified. Even in cases where we're passing an immutable storage value into a pure function, we could skip copying it since we know that the function will not modify the value. |
I have a question about this example: contract Foo:
my_array: u256[100]
def bar(self, mut mem_array: u256[100]):
mem_array[26] = 42
pub def baz(self):
self.bar(self.my_array) # my_array is copied to memory I would expect the compiler to throw an error on |
Ah, yeah good point. This is tricky. In Rust, you would get the error you've described (assuming you don't have ownership issues). We could enforce the same rules as Rust, but it is of course unnecessary since there is an implicit copy happening. I suppose we could consider adding some sort of copy method so it's more clear that you're not actually passing a mutable storage value. contract Foo:
my_array: u256[100]
def bar(self, mut mem_array: u256[100]):
mem_array[26] = 42
pub def baz(self):
self.bar(self.my_array.to_mem()) # my_array is copied to memory |
Also, I'm thinking in terms of ownership here, so This on the other hand would fail: pub def baz(self):
mem_array: u256[100] = self.my_array.to_mem()
self.bar(mem_array) # error: bar would mutate an immutable reference
self.do_something_else(mem_array) |
Good question. I have a hard time thinking of any but it's probably worth reaching out to people that regularly write smart contract code. As a comparision, in Vyper one would use the But, I like the fact that we wouldn't need something like a special decorator for this because it would keep the language simpler and safer after all.
One idea would be to introduce something like Another idea would be something like a decorator
Yeah, I think I'm on board 👍 |
Here's a case from Aragon: https://mobile.twitter.com/izqui9/status/1317826431527886849 |
Ignoring all the mutability stuff for now but I think we should prioritize making One of the planned features for structs is to allow functions on structs (just like in Rust). Now, functions on structs could be very useful for both as instance methods or static functions as demonstrated with the following example.
Taking Usage:
|
Basic |
Proposed Change
Currently methods on contracts have implicit access to
self
as seen in the following snippet.This issue proposes to explicitly require
self
as method argument when a method wants access toself
:Motivation
One of the core motivations is to make
self
less magical (where does it come from?, Why is it a special case?).However there are other compelling reasons why such a change would be beneficial.
Self-less methods as class methods
If a method does not interact with
self
, it can leave outself
and becomes a static method of the contract.This is good for readability and auditing as it becomes easy to spot which methods do and do not interact with contract storage.
Open Question: Would such a method still be callable as
self.does_not_read()
or only asSomething.does_not_read()
?Making mutability explicit
We can go one step further and say that
self
becomes immutable by default. If one wants to have a method that updates a storage variable, one has to explicitly takeself
with themut
keyword as shown below.This again is great for readability and auditability. We can compare that to the inverse of
view
in solidity but I would argue that the nice aspect here is that immutability is the default and one has to add a keyword to make things mutable rather than to add a keyword to declare a method as being view only or pure.The text was updated successfully, but these errors were encountered: