Skip to content

proposal: alternative ergonomic ref count RFC #351

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

Merged
merged 2 commits into from
Jul 21, 2025

Conversation

nikomatsakis
Copy link
Contributor

@nikomatsakis nikomatsakis commented Jul 18, 2025

This goal propses a "friendly fork" of the existing ergonomic ref counting RFC to explore an option that doesn't involve new keywords or changes to the language surface syntax.

@rust-lang/lang -- we should discuss whether this direction makes sense.

Rendered

This goal propses a "friendly fork" of the
existing ergonomic ref counting RFC to explore
an option that doesn't involve new keywords
or changes to the language surface syntax.
Copy link
Member

@lqd lqd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hello sir you've been tricked by the incorrect values in the template, which I've fixed in #349 and is simply awaiting review at your leisure

| Complete seamless implementation | @spastorino | Make `x` equivalent to `x.use` with optional linting |
| Standard reviews | ![Team][] [compiler] | |
| Lang-team champion | @nikomatsakis | |
| Design meetings | ![Team][] [lang] | Two meetings to evaluate both approaches |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI fix

Suggested change
| Design meetings | ![Team][] [lang] | Two meetings to evaluate both approaches |
| Design meeting | ![Team][] [lang] | Two meetings to evaluate both approaches |

Copy link
Contributor

@traviscross traviscross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me on the lang side.

@tomassedovic tomassedovic merged commit 98926a9 into rust-lang:main Jul 21, 2025
2 checks passed
@matthieu-m
Copy link

I feel like this goal is missing one of the key criticism about the lack of explicitness.

In a sense, some of the examples provided here feel like strawmen, as alternative explicit options could exist which would be much more succinct, and it feels that if implicit solutions are to be evaluated, then clearly they should be evaluated against the most succinct explicit solutions, not the least succinct ones.

Should this goal, then, include also offering more succinct, yet still explicit, solutions?

For example:

// listen for dns connections
let _some_a = self.some_a.clone();
let _some_b = self.some_b.clone();
let _some_c = self.some_c.clone();
let _some_d = self.some_d.clone();
let _some_e = self.some_e.clone();
let _some_f = self.some_f.clone();
let _some_g = self.some_g.clone();
let _some_h = self.some_h.clone();
let _some_i = self.some_i.clone();
let _some_j = self.some_j.clone();
tokio::task::spawn(async move {
  	// do something with all the values
});

Could be written with a macro:

let_clone!(self.{some_a, some_b, some_c, some_d, some_e, some_f, some_g, some_h, some_i, some_j});
tokio::task::spawn(async move {
  	//  Use some_a, some_b, some_c, etc... here
});

Or it could be written with a capture clause (somehow):

tokio::task::spawn(async clone(self.{some_a, some_b, some_c, some_d, some_e, some_f, some_g, some_h, some_i, some_j}) move {
  	//  Use some_a, some_b, some_c, etc... here
});

And while, yes, adding/removing a field with the task requires explicitly naming it... it's suddenly far less daunting.


I also feel like this goal, much like the previous one, is conflating two objectives:

  • Necessary language changes to offer lightweight clones.
  • Necessary library changes to provide "built-in" lightweight clones to users.

And I would advocate to focus on the language-level changes, for now, and then discuss separately whether some types in the standard library should be made auto-cloneable.

The tolerance-level for what is cheap, and what is not, varies a lot amongst users. I wouldn't mind others being able to mark their type UseCloned, or to have #![use_cloned] scopes in their code... as long as I can keep it off the codebases I maintain as necessary. In particular, I wouldn't mind Rc being UseCloned, it's cheap, but Arc would be a big problem, and therefore on a RFC to have Arc be UseCloned I would want to advocate for "forking" the type, either exposing an AutoArc or HardArc (depending on the direction) so I can keep using the excellent code of the std without risking accidentally cloning an Arc where it could impact performance... which is of course only a concern if the use is automatic, but not a concern if there's a scope-level attribute to enable UseCloned, and a mild concern if there's an attribute to disable it.

... which I think perfectly illustrates the point that this goal/RFC may get side-tracked if conflating language-level changes and library-level changes, and that perhaps this goal should focus on first establishing how to change the language, and second how to adjust the standard library to match, as the former greatly impacts the discussion on the latter.

@AaronKutch
Copy link

AaronKutch commented Jul 23, 2025

What I do (and this reduced from some private code that should be open sourced in the future) is have a spawn! macro that looks like

 async fn example(rt: Runtime) {
     let x = Arc::new(0);
     let y = Arc::new(0);

     let handle = spawn!(rt, x, y, {
         println!("{x}, {y}");
         rt.yield_now().await;
     });
     handle.join().await.unwrap();

     // the above is equivalent to:

     let handle = {
         let rt = rt.clone();
         let x = x.clone();
         let y = y.clone();
         
         // note the extra `async move` it inserts as well
         spawn(async move {
             println!("{x}, {y}");
             rt.yield_now().await;
         })
     };
     handle.join().await.unwrap();
 }

This works on current stable rust and is fortunately formatted correctly by rustfmt (which has special casing around expressions that are comma delimited). This is also completely explicit and does not require any special traits.

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.

6 participants