21 Nov 2025

feedPlanet Mozilla

Niko Matsakis: Move Expressions

This post explores another proposal in the space of ergonomic ref-counting that I am calling move expressions. To my mind, these are an alternative to explicit capture clauses, one that addresses many (but not all) of the goals from that design with improved ergonomics and readability.

TL;DR

The idea itself is simple, within a closure (or future), we add the option to write move($expr). This is a value expression ("rvalue") that desugars into a temporary value that is moved into the closure. So

|| something(&move($expr))

is roughly equivalent to something like:

{ 
    let tmp = $expr;
    || something(&{tmp})
}

How it would look in practice

Let's go back to one of our running examples, the "Cloudflare example", which originated in this excellent blog post by the Dioxus folks. As a reminder, this is how the code looks today - note the let _some_value = ... lines for dealing with captures:

// task:  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();
tokio::task::spawn(async move {
       do_something_else_with(_some_a, _some_b, _some_c)
});

Under this proposal it would look something like this:

tokio::task::spawn(async {
    do_something_else_with(
        move(self.some_a.clone()),
        move(self.some_b.clone()),
        move(self.some_c.clone()),
    )
});

There are times when you would want multiple clones. For example, if you want to move something into a FnMut closure that will then give away a copy on each call, it might look like

data_source_iter
    .inspect(|item| {
        inspect_item(item, move(tx.clone()).clone())
        //                      ----------  -------
        //                           |         |
        //                   move a clone      |
        //                   into the closure  |
        //                                     |
        //                             clone the clone
        //                             on each iteration
    })
    .collect();

// some code that uses `tx` later...

Credit for this idea

This idea is not mine. It's been floated a number of times. The first time I remember hearing it was at the RustConf Unconf, but I feel like it's come up before that. Most recently it was proposed by Zachary Harrold on Zulip, who has also created a prototype called soupa. Zachary's proposal, like earlier proposals I've heard, used the super keyword. Later on @simulacrum proposed using move, which to me is a major improvement, and that's the version I ran with here.

This proposal makes closures more "continuous"

The reason that I love the move variant of this proposal is that it makes closures more "continuous" and exposes their underlying model a bit more clearly. With this design, I would start by explaining closures with move expressions and just teach move closures at the end, as a convenient default:

A Rust closure captures the places you use in the "minimal way that it can" - so || vec.len() will capture a shared reference to the vec, || vec.push(22) will capture a mutable reference, and || drop(vec) will take ownership of the vector.

You can use move expressions to control exactly what is captured: so || move(vec).push(22) will move the vector into the closure. A common pattern when you want to be fully explicit is to list all captures at the top of the closure, like so:

|| {
    let vec = move(input.vec); // take full ownership of vec
    let data = move(&cx.data); // take a reference to data
    let output_tx = move(output_tx); // take ownership of the output channel

    process(&vec, &mut output_tx, data)
}

As a shorthand, you can write move || at the top of the closure, which will change the default so that closures > take ownership of every captured variable. You can still mix-and-match with move expressions to get more control. > So the previous closure might be written more concisely like so:

move || {
    process(&input.vec, &mut output_tx, move(&cx.data))
    //       ---------       ---------       --------      
    //           |               |               |         
    //           |               |       closure still  
    //           |               |       captures a ref
    //           |               |       `&cx.data`        
    //           |               |                         
    //       because of the `move` keyword on the clsoure,
    //       these two are captured "by move"
    //       
}

This proposal makes move "fit in" for me

It's a bit ironic that I like this, because it's doubling down on part of Rust's design that I was recently complaining about. In my earlier post on Explicit Capture Clauses I wrote that:

To be honest, I don't like the choice of move because it's so operational. I think if I could go back, I would try to refashion our closures around two concepts

  • Attached closures (what we now call ||) would always be tied to the enclosing stack frame. They'd always have a lifetime even if they don't capture anything.
  • Detached closures (what we now call move ||) would capture by-value, like move today.

I think this would help to build up the intuition of "use detach || if you are going to return the closure from the current stack frame and use || otherwise".

move expressions are, I think, moving in the opposite direction. Rather than talking about attached and detached, they bring us to a more unified notion of closures, one where you don't have "ref closures" and "move closures" - you just have closures that sometimes capture moves, and a "move" closure is just a shorthand for using move expressions everywhere. This is in fact how closures work in the compiler under the hood, and I think it's quite elegant.

Why not suffix?

One question is whether a move expression should be a prefix or a postfix operator. So e.g.

|| something(&$expr.move)

instead of &move($expr).

My feeling is that it's not a good fit for a postfix operator because it doesn't just take the final value of the expression and so something with it, it actually impacts when the entire expression is evaluated. Consider this example:

|| process(foo(bar()).move)

When does bar() get called? If you think about it, it has to be closure creation time, but it's not very "obvious".

We reached a similar conclusion when we were considering .unsafe operators. I think there is a rule of thumb that things which delineate a "scope" of code ought to be prefix - though I suspect unsafe(expr) might actually be nice, and not just unsafe { expr }.

Edit: I added this section after-the-fact in response to questions.

Conclusion

I'm going to wrap up this post here. To be honest, what this design really has going for it, above anything else, is its simplicity and the way it generalizes Rust's existing design. I love that. To me, it joins the set of "yep, we should clearly do that" pieces in this puzzle:

These both seem like solid steps forward. I am not yet persuaded that they get us all the way to the goal that I articulated in an earlier post:

"low-level enough for a Kernel, usable enough for a GUI"

but they are moving in the right direction.

21 Nov 2025 10:45am GMT

The Servo Blog: Servo Sponsorship Tiers

The Servo project is happy to announce the following new sponsorship tiers to encourage more donations to the project:

Organizations and individual sponsors donating in these tiers will be acknowledged on the servo.org homepage with their logo or name. Please note that such donations should come with no obligations to the project i.e they should be "no strings attached" donations. All the information about these new tiers is available at the Sponsorship page on this website.

Please contact us at join@servo.org if you are interested in sponsoring the project through one of these tiers.

Use of donations is decided transparently via the Technical Steering Committee's public funding request process, and active proposals are tracked in servo/project#187.

Last, but not least, we're excited to welcome our first bronze sponsor LambdaTest who has recently started donating to the Servo project. Thank you very much!

21 Nov 2025 12:00am GMT

20 Nov 2025

feedPlanet Mozilla

Mozilla Localization (L10N): Localizer spotlight: Robb

About You

My profile in Pontoon is robbp, but I go by Robb. I'm based in Romania and have been contributing to Mozilla localization since 2018 - first between 2018 and 2020, and now again after a break. I work mainly on Firefox (desktop and mobile), Thunderbird, AMO, and SUMO. When I'm not volunteering for open-source projects, I work as a professional translator in Romanian, English, and Italian.

Getting Started

Q: How did you first get interested in localization? Do you remember how you got involved in Mozilla localization?

A: I've used Thunderbird for many years, and I never changed the welcome screen. I'd always see that invitation to contribute somehow.

Back in 2018, I was using freeware only - including Thunderbird - and I started feeling guilty that I wasn't giving back. I tried donating, but online payments seemed shady back then, and I thought a small, one-time donation wouldn't make a difference.

Around the same time, my mother kept asking questions like, "What is this trying to do on my phone? I think they're asking me something, but it's in English!" My generation learned English from TV, Cartoon Network, and software, but when the internet reached the older generation, I realized how big of a problem language barriers could be. I wasn't even aware that there was such a big wave of localizing everything seen on the internet. I was used to having it all in English (operating system, browser, e-mail client, etc.).

After translating for my mom for a year, I thought, why not volunteer to localize, too? Mozilla products were the first choice - Thunderbird was "in my face" all day, all night, telling me to go and localize. I literally just clicked the button on Thunderbird's welcome page - that's where it all started.

I had also tried contributing to other open-source projects, but Mozilla's Pontoon just felt more natural to me. The interface is very close to the CAT tools I am used to.

Your Localization Journey

Q: What do you do professionally? How does that experience influence your Mozilla work and motivate you to contribute to open-source localization?

A: I've been a professional translator since 2012. I work in English, Romanian, and Italian - so yes, I type all the time.

In Pontoon, I treat the work as any professional project. I check for quality, consistency, and tone - just like I would for a client.

I was never a writer. I love translating. That's why I became a translator (professionally). And here… I actually got more feedback here than in my professional translation projects. I think that's why I stayed for so long, that's why I came back.

It is a change of scenery when I don't localize professionally, a long way from the texts I usually deal with. This is where I unwind, where I translate for the joy of translation, where I find my translator freedom.

Q: At what moment did you realize that your work really mattered?

A: When my mom stopped asking me what buttons to click! Now she just uses her phone in Romanian. I can't help but smile when I see that. It makes me think I'm a tiny little part of that confidence she has now.

Community & Collaboration

Q: Since your return, Romanian coverage has risen from below 70% to above 90%. You translate, review suggestions, and comment on other contributors' work. What helps you stay consistent and motivated?

A: I set small goals - I like seeing the completion percentage climb. I celebrate every time I hit a milestone, even if it's just with a cup of coffee.

I didn't realize it was such a big deal until the localization team pointed it out. It's hard to see the bigger picture when you work in isolation. But it's the same motivation that got me started and brought me back - you just need to find what makes you hum.

Q: Do you conduct product testing after you localize the strings or do you test them by being an active user?

A: I'm an active user of both Firefox and Thunderbird - I use them daily and quite intensely. I also have Firefox Nightly installed in Romanian, and I like to explore it to see what's changed and where. But I'll admit, I'm not as thorough as I should be! Our locale manager gives me a heads-up about things to check which helps me stay on top of updates. I need to admit that the testing part is done by the team manager. He is actively monitoring everything that goes on in Pontoon and checks how strings in Pontoon land in the products and to the end users.

Q: How do you collaborate with other contributors and support new ones?

A: I'm more of an independent worker, but in Pontoon, I wanted to use the work that was already done by the "veterans" and see how I could fit in. We had email conversations over terms, their collaboration, their contributions, personal likes and dislikes etc. I think they actually did me a favor with the email conversations, given I am not active on any channels or social media and email was my only way of talking to them.

This year I started leaving comments in Pontoon - it's such an easy way to communicate directly on specific strings. Given I was limited to emails until now, I think comments will help me reach out to other members of the team and start collaborating with them, too.

I keep in touch with the Romanian managers by email or Telegram. One of them helps me with technical terms, he helped get the Firefox project to 100% before the deadline. He contacts me with information on how to use options (I didn't know about) in Pontoon and ideas on wording (after he tests and reviews strings). Collaboration doesn't always mean meetings; sometimes it's quiet cooperation over time.

Mentoring is a big word, but I'm willing for the willing. If someone reaches out, I'll always try to help.

Q: Have you noticed improvements in Pontoon since 2020? How does it compare to professional tools you use, and what features do you wish it had?

A: It's fast - and I love that.

There's no clutter - and that's a huge plus. Some of the "much-tooted" professional tools are overloaded with features and menus that slow you down instead of helping. Pontoon keeps things simple and focused.

I also appreciate being able to see translations in other languages. I often check the French and Italian versions, just to compare terms.

The comments section is another great feature - it makes collaboration quick and to the point, perfect for discussing terms or string-specific questions. Machine translation has also improved a lot across the board, and Pontoon is keeping pace.

As for things that could be better - I'd love to try the pre-translation feature, but I've noticed that some imported strings confirm the wrong suggestion out of several options. That's when a good translation-memory cleanup becomes necessary. It would be helpful if experienced contributors could trim the TM, removing obsolete or outdated terms so new contributors won't accidentally use them.

Pontoon sometimes lags when I move too quickly through strings - like when approving matches or applying term changes across projects. And, unlike professional CAT tools, it doesn't automatically detect repeated strings or propagate translations for identical text. That's a small but noticeable gap compared to professional tools.

Personal Reflections

Q: Professional translators often don't engage in open-source projects because their work is paid elsewhere. What could attract more translators - especially women - to contribute?

A: It's tricky. Translation is a profession, not a hobby, and people need to make a living.

But for me, working on open-source projects is something different - a way to learn new things, use different tools, and have a different mindset. Maybe if more translators saw it as a creative outlet instead of extra work, they'd give it a try.

Involvement in open source is a personal choice. First, one has to hear about it, understand it, and realize that the software they use for free is made by people - then decide they want to be part of that.

I don't think it's a women's thing. Many come and many go. Maybe it's just the thrill at the beginning. Some try, but maybe translation is not for them…

Q: What does contributing to Mozilla mean to you today?

A: It's my way of giving back - and of helping people like my mom, who just want to understand new technology without fear or confusion. That thought makes me smile every time I open Firefox or Thunderbird.

Q: Any final words…

A: I look forward to more blogs featuring fellow contributors and learning and being inspired from their personal stories.

20 Nov 2025 6:46pm GMT