Skip to content

Allow ref (&) parameters in contract fns#1535

Merged
leighmcculloch merged 45 commits intomainfrom
ref-arg-support
Oct 29, 2025
Merged

Allow ref (&) parameters in contract fns#1535
leighmcculloch merged 45 commits intomainfrom
ref-arg-support

Conversation

@leighmcculloch
Copy link
Copy Markdown
Member

@leighmcculloch leighmcculloch commented Aug 11, 2025

What

Allow contract functions parameters to be immutable references, rather than only owned values.

Why

This change is primarily about making it more convenient to write contract functions like typical Rust functions. In Rust function parameters can be of a few variations, they can be an owned value or a reference/borrow, and can be immutable or mutable. Today contract function parameters must be owned and immutable.

Reference parameters are useful when needing to pass around a value that the caller would like to retain ownership over. Mutable parameters are helpful when the function will mutate the parameter making the mutated value available to the caller.

It is common when writing small composable Rust functions to use ref and mut as needed.

When writing contract functions the composability of the functions being written is restricted. Owned values must be used, and this leads to situations where functions must received cloned values.

In contracts cloning SDK types when passing them around is not a performance concern since all SDK types are a small integer value. But it is inconvenient and it makes contracts look less like regular Rust code when there is an abundance of .clone() for no apparent reason other than the contract functions don't let you.

Is this a big issue? No, not particularly. But in general the soroban-sdk tries to let people write Rust how they'd write Rust elsewhere. And this change lifts a limitation that isn't really required.

Why not

This change introduces variance into the trait-compatible interface that implementations can take. For a contract interface that is intended to be standard, one implementation might use all owned values, another might use all refs, and another might use any combination. This doesn't affect a contracts ability to be called because this information does not make it into the exported interface. But if someone was to share a Rust trait to match one implementation, there's a good chance it would fail to match other implementations. Technically variance already exists today. The env: Env parameter is already optional, and can already take either an owned Env or a ref &Env. So the ship already sailed from day one.

You could argue that the top-level entry point value in a program should always be owned. Someone has to own every value. The fact that contract functions do not need to own the value is because there's actually an outer function that gets exported that is truly the entry point and that function can own the value. But in some ways that's a bit odd.

Merging

This change can be merged at anytime and can be released in a minor release. It does not change or limit any existing behaviour and only adds new behaviour that is disallowed today.

Thanks

Thanks to @willemneal contributing #1521 that this change is derived.

Todo

@willemneal
Copy link
Copy Markdown
Contributor

Love the added mutability! One future idea from this and it might make things too complex, but it would be amazing if &mut Env would be able to determine whether a method could write to storage/publish events. I assume at this point it would be too much of a breaking change, but it would be a very nice way to enforce in the types what you are allowed to do in a method.

@leighmcculloch
Copy link
Copy Markdown
Member Author

but it would be amazing if &mut Env would be able to determine whether a method could write to storage/publish events

Agreed, but I think that's a bigger thing to tackle, and the timing isn't right. Lots of complexity, and with unclear benefits. There's a discussion over here discussing the idea, but I am not chasing it in the near future:

github-merge-queue bot pushed a commit that referenced this pull request Sep 30, 2025
### What
Add a job that expands the generated code of each test wasm both when
built locally for the host with tests, and when built for wasm, and
verifies if the committed files are up-to-date.

  ### Why
So that PRs show diffs when there are changes to generated code. To
catch unexpected and make hyper-visible the impact of changes to macros.

There's always an argument with this type of files we're committing that
it will get too noisy, too cumbersome, and that it won't be the right
balance and tradeoff in maintainence cost. This is an experiment to some
degree and I think we'll find out during Q4 if it pays off or not and
then we can retro, because we have changes planned that'll change the
generated code (e.g. #1507 #1535) and we'll see how it goes. We can
decide to remove this if the tradeoff doesn't end up being right.

For #1544

Dependent on:
- stellar/binaries#45
- #1580
@leighmcculloch
Copy link
Copy Markdown
Member Author

Something I noticed out of this change is that I missed adding support for returning & on functions, which should be possible. I'll have a look at potentially expanding the change to support that, but might defer it to another PR or to never depending on what I find.

@dmkozh
Copy link
Copy Markdown
Contributor

dmkozh commented Oct 22, 2025

Copy is a marker trait that indicates that the value can be copied by bits. It requires that all internal components are also Copy. The Env contains the Host, which contains an Rc and so on. Every type would have to be Copy. Rc and many other types used internally are not.

Hmm, I see, yeah, it would make sense in the 'guest' mode, but it indeed seems infeasible to have it in the 'host' mode for tests, so probably it's not worth pursuing in general.

Something I noticed out of this change is that I missed adding support for returning & on functions, which should be possible.

What would be the lifetime of that reference? I'm not sure I understand how that could work in a non-confusing way, but I guess I'll wait until the change is made.

@leighmcculloch
Copy link
Copy Markdown
Member Author

leighmcculloch commented Oct 29, 2025

Discovered today that mut parameters are actually supported today, without the ref, although it has some issues captured in:

I'm blocking this change on the following change that addresses those so that this change is operating on a better foundation with a better test bas:

@leighmcculloch leighmcculloch marked this pull request as draft October 29, 2025 04:56
@leighmcculloch leighmcculloch changed the base branch from main to i1605-add-mut-arg-test October 29, 2025 04:56
@leighmcculloch
Copy link
Copy Markdown
Member Author

maybe & and mut are alright, but I still really dislike the &mut as it's misleading for the reader

The pr now disallows &mut while continuing to allow & and mut.

Disallowing &mut required adding functionality. The changes to allow & inherently also allows &mut, so to disallow &mut an additional check must be inserted to create an error in that case.

@leighmcculloch leighmcculloch changed the title Allow ref and mut parameters in contract fns Allow ref (&) parameters in contract fns Oct 29, 2025
@leighmcculloch leighmcculloch marked this pull request as ready for review October 29, 2025 06:57
@leighmcculloch leighmcculloch marked this pull request as draft October 29, 2025 06:57
Base automatically changed from i1605-add-mut-arg-test to main October 29, 2025 17:06
@leighmcculloch leighmcculloch marked this pull request as ready for review October 29, 2025 20:41
@leighmcculloch leighmcculloch requested a review from dmkozh October 29, 2025 20:42
@leighmcculloch leighmcculloch added this pull request to the merge queue Oct 29, 2025
Merged via the queue into main with commit a9f2b50 Oct 29, 2025
88 checks passed
@leighmcculloch leighmcculloch deleted the ref-arg-support branch October 29, 2025 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants