# Notes on partial borrows

**URL:** <https://internals.rust-lang.org/t/notes-on-partial-borrows/20020>\
**Category:** language design\
**Created:** [December 12, 2023, 7:53pm UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020 "2023-12-12T19:53:31Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)\
**Post date:** [December 12, 2023, 7:53pm UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/1 "2023-12-12T19:53:31Z")

</div>

One of the borrow checker's major limitations is that it's not possible to create a reference that borrows from a subset of a type (such that borrows of non-overlapping subsets will not conflict). A langauge-level solution to this problem has long been desired.

For example, given a `struct Foo { x: i32, y: String, z: u32 }`, here's a (non-exhaustive) list of things people might want to express:

- A reference to a `Foo` takes a shared borrow of fields `x` and `y` only, both for lifetime `'a`
- A reference to a `Foo` that takes a shared borrow of field `x` and a mutable borrow of field `z`, both for lifetime `'a`
- A reference to a `Foo` that takes a shared borrow of field `x` for lifetime `'a`, and a mutable borrow of field `y` for lifetime `'b`

Prior discussion:

- @nikomatsakis's [blog post](https://smallcultfollowing.com/babysteps/blog/2021/11/05/view-types/) that started it all, and its [comment thread](https://internals.rust-lang.org/t/blog-post-view-types-for-rust/15556). I would suggest reading that post (if you haven't already done so) before continuing with this one.
- [https://blog.yoshuawuyts.com/safe-pin-projections-through-view-types/](https://blog.yoshuawuyts.com/safe-pin-projections-through-view-types/) and [its comment thread](https://internals.rust-lang.org/t/view-types-based-on-pattern-matching/16879)
- [https://internals.rust-lang.org/t/view-types-give-borrow-checker-more-information-about-your-code/16665](https://internals.rust-lang.org/t/view-types-give-borrow-checker-more-information-about-your-code/16665)
- [https://internals.rust-lang.org/t/view-types-based-on-pattern-matching/16879](https://internals.rust-lang.org/t/view-types-based-on-pattern-matching/16879)
- Probably plenty more I'm missing

There are two potential approaches here. Either the referent (`T` in `&T`/`&mut T`) specifies what can be accessed—usually called "view types"; or the referee (the `&`/`&mut` itself) does—I'll call this "reference views".

# Approach 1: View types

Niko Mastakis's blog post takes the first approach, with the following justification:

> When I’ve thought about this problem in the past, I’ve usually imagined that the list of “fields that may be accessed” would be attached to the _reference_. But that’s a bit odd, because a reference type `&mut T` doesn’t itself have an fields. The fields come from `T`.

However, this model has several issues:

- Mutability gets specified twice, as part of both the reference and the view.
  - This inconvenience is balanced by the advantage of being able to specify mutability restrictions for owned values; but [RFC 3323](https://rust-lang.github.io/rfcs/3323-restrictions.html) likely makes that feature redundant.

- It's impossible to have a reference that borrows multiple fields for different lifetimes.
- How should owned view types be treated from an opsem perspective? Can they be `memcpy`ed? Transmuted to `[MaybeUninit<u8>]`? Stored in a `Box`? `mem::swap`ped? `drop()`ped? etc.

For these reasons, I believe that Niko's original instincts were correct, and putting the view on the reference is the right choice.

# Approach 2: Reference views

## Placeholder syntax

What might "reference views" look like? The `&'a T`/`&'a mut T` family would be extended with many more access modes. I won't make a full syntax proposal in this post. But for the sake of examples, this is the notation I'll be using, given the definition of `Foo` from earlier:

| Field `x` | Field `y` | Field `z` | Reference type |
| --- | --- | --- | --- |
| Shared borrow for `'a` | Shared borrow for `'a` | Not borrowed | `&<'a x, 'a y> Foo` |
| Shared borrow for `'a` | Not borrowed | Mutable borrow for `'a` | `&<'a x, 'a mut z> Foo` |
| Not borrowed | Mutable borrow for `'a` | Mutable borrow for `'b` | `&<'a mut y, 'b mut z> Foo` |

`&'a Foo` is equivalent to `&<'a x, 'a y, 'a z> Foo`; similarly, `&'a mut Foo` is equivalent to `&<'a mut x, 'a mut y, 'a mut z> Foo`. (Or are they? See "Aliasing model" section.) `&<> Foo` is a reference with access to nothing.

## Subtyping implications

For view references to be usable, a function that accepts them should be strictly more flexible than one that accepts the correspoding non-view reference. For example:

- `fn foo<'a>(_: &<'a x> Foo)` is more flexible than `fn foo<'a>(_: &<'a x, 'a y> Foo)` is more flexible than `fn foo<'a>(_: &'a Foo)`.

- `fn foo<'a>(_: &<'a mut z> Foo)` is more flexible than `fn foo<'a>(_: &<'a mut y, 'a mut z> Foo)` is more flexible than `fn foo<'a>(_: &'a mut Foo)`.

Function signatures are contravariant in their inputs, so the above implies that:

- `&<'a x> Foo` is a supertype of `&<'a x, 'a y> Foo` is a supertype of `&'a Foo`.
- `&<'a mut x> Foo` is a supertype of `&<'a mut x, 'a mut y> Foo` is a supertype of `&'a mut Foo`.

How does this extend to views with both shared and mutable portions? First of all, views compose:

- `fn foo<'a>(_: &<'a x> Foo)` and `fn foo<'a>(_: &<'a mut y> Foo)` are more flexible than `fn foo<'a>(_: &<'a x, 'a mut y> Foo)`.

Therefore,

- `&<'a x> Foo` and `&<'a mut y> Foo` are supertypes of `&<'a x, 'a mut y> Foo`.

But, what's the relationship between `&<'a x> Foo` and `&<'a mut x> Foo`? Well,

- Logically, `fn foo<'a>(_: &<'a x> Foo)` should be more flexible than `fn foo<'a>(_: &<'a mut x> Foo)`, right?
- So, `&<'a x> Foo` is a supertype of `&<'a mut x> Foo`.
- Therefore, `&<'a x, 'a y, 'a z> Foo` is a supertype of `&<'a mut x, 'a mut y, 'a mut z> Foo`.
- Therefore, `&'a Foo` is a supertype of `&'a mut Foo`.
- Therefore, `&'b &'a Foo` is a supertype of `&'b &'a mut Foo`.
- But [that would be unsound](https://github.com/rust-lang/unsafe-code-guidelines/issues/270#issuecomment-759769675).

Therefore, either substituting `fn foo<'a>(_: &<'a mut x> Foo)` with `fn foo<'a>(_: &<'a x> Foo)` must be forbidden, or the variance rules must give view mutability special treatment.

## Monomorphization and `TypeId`

Under this scheme, `&'a T` and `&'a mut T` are equivalent modulo views. Yet they also have different `TypeId`s. Therefore, we must say that references that differ in views have distinct `TypeId`s and are monomorphized separately.

## Multiple-lifetime views

Consider the following trait, and its implementation for `Foo`:

```rust
trait Frob {
    fn frob<'a>(&'a mut self) -> &'a mut u32;
}

impl Frob for Foo {
    fn frob<'a>(&'a mut self) -> &'a mut u32 {
        *self.x += 1;
        &mut self.z
    }
}

```

As written, `Foo::frob`'s signature requires that `self` remain mutably borrowed, and therefore inaccessible, in its entirety for as long as the returend `&mut u32` is live.

```rust
fn test(f: Foo) {
   let mut_u32 = f.frob();
   dbg!(&f.x); // ERROR: f is mutably borrowed
   dbg!(mut_u32);
}

```

This is needlessly restrictive, however: the returned mutable borrow only involves the `z` field. The `Frob` implementation could be updated to use a view reference to reflect this:

```rust
impl Frob for Foo {
    fn frob<'a: 'b, 'b>(&<'b mut x, 'a mut z> self) -> &'a mut u32 {
        *self.x += 1;
        &mut self.z
    }
}

```

In the updated signature, the returned `&mut` only references `'a`; `self.x` was borrowed for `'b`, not `'a`, so it can be borrowed again as soon as `frob` returns, even while the `&'a mut u32` is still live.

```rust
fn test(f: Foo) {
   let mut_u32 = f.frob();
   dbg!(&f.x); // works! only `f.z` is still borrowed.
   dbg!(mut_u32);
}

```

However, the new signature introduced a new lifetime parameter `'b` to `frob`'s signature. As long as the parameter is late-bound (as it is in this example), this transformation is at most a minor breaking change (might break turbofish); but if it's early-bound, it could be major breakage (I think?).

## Aliasing model

A view reference like `&<x, mut z> Foo` might have access to several discontiguous regions of memory, and there could be another reference like `&<y> Foo` concurrently accessing memory in the "gap" between `x` and `z`. The Rust aliasing model, and the annotations that Rust emits to LLVM, would need to reflect this.

One question that would need an answer is which view references are allowed to read or write padding bytes. There are several options here:

- View references have no access to padding, only full `&T` and `&mut T` do. (This means that `&T`/`&mut T` are no longer equivalent types to a view over all fields.)
- Only a view over all fields has access to padding, and this access is mutable only if all the views are mutable-access.
- A reference with view of two fields separated only by padding has view over that padding, and that view if mutable iff the view to both fields is mutable. Access to padding at the beginning or end of a struct is granted by view over its first or last field (as laid out in memory) respectively.
- References can access the padding immediately following each field they can view (with the corresponding mutability); access to padding before the first field is controlled by access to the first field.
- Etc…

# Conclusion

I have no plans to develop this into a full proposal, but I hope these notes are helpful to anyone else who decides to do so. I would appreciate people's thoughts, especially on view types versus reference views, mutability subtyping, and aliasing-model questions.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [December 13, 2023, 3:25am UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/2 "2023-12-13T03:25:25Z")

</div>

> [@Jules-Bertholet](#):
>
> Aliasing model

(Member of T-opsem; NON-NORMATIVE thoughts.)

To note, both the SB and TB proposed models operate on a per-byte basis, so discontinuous borrows aren't intrinsically problematic. (In fact, it's not impossible that we'll permit discontinuous _allocated object_s.) As a lower bound, `&<x, mut y> Foo` can behave like `(&x, &mut y)`.

But padding would need to be addressed somehow. It's an identical question to that which arises with `UnsafeCell` — we permit deallocation of borrowed memory if it's entirely covered by `UnsafeCell` (rationale: `Arc` getting freed while another thread is still in `AtomicUsize::fetch_sub`) — is padding ever considered “covered” by `UnsafeCell` when the type's fields are? It would imo be nonideal if the field adjacency rules for padding's retag `UnsafeCell`ness differs from it's `mut`ness (though they do have different trade-offs and implications, potentially justifying a difference).

`UnsafeCell` has a bit of a cop-out option available, where `UnsafeCell` is (w.r.t. validity) “infectious,” and a reborrow is always either entirely “`Freeze`” or not\[1\]. (This is how rustc and LLVM work today.) The same cop-out _mostly_ can't work for borrow views without weakening uniqueness guarantees\[2\].

...Except that maybe it can, by utilizing similar machinery to how TB fixes the “`&Header` problem” (also `extern type`). In (overly reductive) short, SB _provisionally_ retags “extended” provenance range, but only actually (retroactively) commits the effects of that reborrow (i.e. invalidation of other provenance) when those bytes are accessed. It might be possible to extend that behavior to padding. For the same reason as `&Header`, it should definitely apply to view-excluded fields\[3\].

I think the most straightforward approach would be to include padding in the retag of its minimally permissive neighbor. That's equivalent to your third option. If you allow

Also, `&<> Foo` might be more interesting than `&()` — specifically, it would be reasonable for it to apply an “exists” guard (LLVM `dereferencable(size_of)`) to the `Foo` allocation, even if it doesn't otherwise eagerly retag any bytes.

> Multiple-lifetime views

Could this potentially solve the `&mut T -> &T` “borrow downgrade” case as well? E.g. `fn foo(&<'a mut self, 'b self> T) -> &'b T`. TB is already equipped to handle this, as it treats all `&mut` borrows as “three phase” (c.f. two-phase borrows).

> View Types vs Reference Views

Just to note, “enum variants as types” is quite similar to views as types. You can almost model enum variant types as view types, where the view is the fields of the variant; the limiting factor is the special handling of the virtual discriminant field.

I believe rustc is also more equipped to deal with type refinement than reference refinement (e.g. `Freeze`/`Unpin` are qualities of the type at compile time despite being properties of the borrow at runtime). But on the other hand, the existence of `&Foo.{x, y}` implies the ability to manipulate `Foo.{x, y}` by value\[4\]... which might be useful for `self` methods that only consume a subset of fields?

> [@Jules-Bertholet](#):
>
> but RFC 3323 [(restricted mutability)] likely makes that feature redundant

Not really? That's about more granular access/visibility control. It does have some overlap, but doesn't cover all expressible cases, such as a method only partially moving from `self`.

View types _theoretically_ aren't that complicated, as they're “just” the use of “preserved” padding instead of “clobbered” padding. They also vastly simplify the interaction with generics, as `for<T> &T` can monomorphize to `&Foo.{x,y}` or `&Foo.{z}` just fine, but you'd probably both `&<'_ x, '_ y> Foo` and `&<'_ z> Foo` have `T=Foo`, and it's the `&` which differs. Add a trait bound and now this really matters, since e.g. `Debug::debug` takes an unrestricted `&T` as input.

> Subtyping implications

Also, subtyping opens up some impressively annoying questions w.r.t. `impl` applicability. Pattern restricted types (and enum variant types, which are a special case of such) are (last I saw) struggling with figuring out reasonable rules; it's important for availability of universal traits like `Debug`, `Eq`, and `Any`.

Lifetime variance doesn't cause problems because of the limited way of constraining impl applicability. You can constrain the “top,” but only relatively; it's always an applicable impl with all `'static` lifetimes. You can constrain the “bottom,” but only by requiring it be exactly `'static`. So you can only ever have one impl apply to the variant type family.

I think pattern restricted types have so far leant towards just forbidding any impls on restricted types, requiring you to use a newtype to write any new impls. (The newtype is also necessary to enable use of any created niches, as without it's a subtype of the unrestricted type.) But a) “pattern restriction safe” (i.e. no `&mut self` methods) is a new quality (and breakage axis) for traits that doesn't currently exist, and b) pattern restricted types are subtypes of the unrestricted type, whereas view restricted types are supertypes of the unrestricted type, thus the existing impls are for the subtype, not the supertype, making our case fundamentally inverse pattern restricted types' anyway.

> Conclusion

> [@Jules-Bertholet](#):
>
> I have no plans to develop this into a full proposal, but I hope these notes are helpful to anyone else who decides to do so.

Fully understandable. This is the closest I've been to believing view restrictions are plausible, so it's good discussion. The big additional thing I'd personally want to see for a full proposal is some resolution to how impl selection and coherence should work with views.

It's also probably worthwhile to mention how view restrictions differ from pattern restrictions, and how we'll differentiate the two. They're very different – the restrictions are going in opposite directions — but that's not immediately obvious from just syntax, especially since `mut` is a part of pattern syntax.

* * *

1. But that does open a bit of a question: should `UnsafeCell` still “infect” a viewed reborrow which excludes any `UnsafeCell` fields? 

2. It's considered reasonably acceptable for `UnsafeCell` (i.e. shared mutable) to be cross field infectious because it being cross variant infectious is effectively a necessity (such that reborrows don't need to read the (potentially niched) discriminant). But it's not _100%_ a given, as it's possible to use that granularity for optimization, even if LLVM can't (currently). 

3. Which does build a bit of a footgun if `UnsafeCell` doesn't infect excluding views, since the field would be retagged as if it weren't covered by `UnsafeCell`. (This is necessary for LLVM `noalias` semantics, which can't be span restricted.) 

4. By value views should invariant over their field set because they'd forget to drop any additionally provided fields. Which is _interesting_ combined with by reference views being variant.

---

<div class="post-metadata">

**Author:** ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)\
**Post date:** [December 13, 2023, 8:19am UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/3 "2023-12-13T08:19:29Z")

</div>

> [@CAD97](#):
>
> Could this potentially solve the `&mut T -> &T` “borrow downgrade” case as well?

I very much hope so! Though the scheme I've described is insufficiently general to handle all cases of this. For example, `fn frob<'a: 'b, 'b>(&<'b mut x, 'a z> self) -> &'a u32` would have only the shared borrow persist past the end of the function, but mutable access to `z` would be forbidden within the function. To fully address all cases, you would want a view like `&<'b mut 'a x> Foo`, where the reference can access `Foo` for a length of `'a` but that access is mutable only for the length of `'b`.

> [@CAD97](#):
>
> Just to note, “enum variants as types” is quite similar to views as types. You can almost model enum variant types as view types, where the view is the fields of the variant; the limiting factor is the special handling of the virtual discriminant field.

Now I'm wondering, would it make sense to have a view type that grants access to fields in several variants of an enum, conditional on the enum being in that variant? All such view references would have a read view of the discriminant (and would thus conflict with a full `&mut`, which would be the only way to get write access to the discriminant). I think the aliasing model would break this, but maybe I'm wrong?

> [@CAD97](#):
>
> `self` methods that only consume a subset of fields

Consuming something means assuming responsibility for dropping it—which, as you note, means that a by-value view type would need to be invariant in the view. This makes them useless for refining existing API contracts. Given that fact, I struggle to see a use-case.

> [@CAD97](#):
>
> `impl` applicability

Actually, I think reference views run into none of the problems that pattern types have here.

- View references, despite their subtyping relationships, have unique `TypeId`s. This means we can allow writing distinct impls for different views of the same type without any soundness issues. HRTB subtyping already works like this (with a [FCW](https://github.com/rust-lang/rust/issues/56105), but there's no remaining unsoundness).
- As you say, view references are supertypes of the standard references. This means, among other things, that implementing most of the standard traits for them (`Eq`, `Debug` etc) makes very little sense. Non-`mut` view references would need `Copy` and `Clone` impls, and `Send`/`Sync` would also need to be properly handled, but I think that more or less covers it.

> [@CAD97](#):
>
> It's also probably worthwhile to mention how view restrictions differ from pattern restrictions, and how we'll differentiate the two.

Pattern restrictions go on the type, view restrictions go (I propose) on the reference. This distinct placement makes them easy to tell apart: `&<view> Type is pattern`.

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [December 13, 2023, 9:45am UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/4 "2023-12-13T09:45:49Z")

</div>

> [@CAD97](#):
>
> (In fact, it's not impossible that we'll permit discontinuous _allocated object_s.)

Okay this is as off topic as it gets, but I really really wanted that! Mainly for storing the tag of an enum separated from the payload, but still work with safe Rust. But also:

> [@CAD97](#):
>
> View types _theoretically_ aren't that complicated, as they're “just” the use of “preserved” padding instead of “clobbered” padding.

If we could have multiple structs at the same memory location, but whose fields were at non-overlapping memory offsets (like view types, but for owned structs), so that the fields of struct A would be considered to be padding by struct B (and vice versa)... that would be pretty cool. Maybe even a language feature to split a struct into two or more structs located at the same memory address. For example, splitting `struct X(i32, f32)` into a struct that has only a field `i32` at offset 0 (and then padding), and a struct that has only a field `f32` at another offset, both at the same memory location (and thus can be accessed at the same time in safe Rust)

Well now this doesn't seem so off topic anymore: this is just an owned version of view types, and then view types proper are a borrow of those split structs.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [December 13, 2023, 2:29pm UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/5 "2023-12-13T14:29:23Z")

</div>

> [@Jules-Bertholet](#):
>
> Under this scheme, `&'a T` and `&'a mut T` are equivalent modulo views. Yet they also have different `TypeId`s. Therefore, we must say that references that differ in views have distinct `TypeId`s and are monomorphized separately.

I had thought about view types, too, and this is a concern I came up with. I feel like treating different views with different `TypeId`s has the potential to overdo on monomorphization.

Of course, `&'a T` and `&'a mut T` already have different `TypeId`s, but maybe one can avoid giving a new one to each kind of view type, too.

And/or maybe there are in general improvements to be had around cutting down on monomorphization. `TypeId`s and specialization seem to be the main thing hindering this possibility.

Again, for view types in particular, any `&'static<…some view…> Foo` would be a `'static` type, and thus with the current `Any` API, different views require different `TypeId`s based on that observation alone. I’ve been contemplating whether it’s possible to de-couple `T: 'static` from `T: Any` (with a backwards-compatibility story where prior to edition X, a `T: 'static` bound [even an implied one] sort-of desugars to `T: Any`). But that seems hard, too.

---

<div class="post-metadata">

**Author:** ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)\
**Post date:** [December 13, 2023, 6:14pm UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/6 "2023-12-13T18:14:35Z")

</div>

> [@steffahn](#):
>
> I feel like treating different views with different `TypeId`s has the potential to overdo on monomorphization.

I suspect this won't be a huge issue either. View references (as I'm imagining them) won't implement most traits, and won't be created unless you explicitly use the "view reference" operator. I don't expect them to show up everywhere. (This is in contrast to [pattern types](https://gist.github.com/joboet/0cecbce925ee2ad1ee3e5520cec81e30), which will absolutely show up everywhere, absolutely cannot afford to have different `TypeId`s, and where dealing with `Any` is absolutely a problem I have no idea how to solve.)

---

<div class="post-metadata">

**Author:** ![VitWW](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/vitww/32/10822_2.png) [@VitWW](https://internals.rust-lang.org/u/VitWW)\
**Post date:** [December 14, 2023, 4:59am UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/7 "2023-12-14T04:59:56Z")

</div>

@Jules-Bertholet I've created 2 variants of partial types for resolving partial-borrow problem (closed in Rfcs). Now, the third version is in progress/draft - [partial\_types3](https://github.com/VitWW/rfcs/blob/partial_types3/text/0000-partial_types.md).

Main idea - get an **mathematical guarantee** to borrow-checker that borrowing the whole variable with partial type and pretending to borrow just permitted fields is **fully safe**

And since it is a guarantee by **type** , not by **values** , it has _zero cost_ in binary! Any type error is a compiler error, so no errors in the runtime.

The core Idea of this proposal is "Proxy Borrowing" - we borrow the whole variable, but borrow-checker pretends it borrow just permitted fields of this variable.

And Type-checker gives a mathematical guarantee, than all fields with no-access to read remain intact and all partly-immutable fields remain immutable!

And this mean, that Proxy Borrowing borrowing is fully **safe** and _zero cost_ in binary.

How it looks:

```rust
struct StructABC { a: u32, b: i64, c: f32, }

// function with partial parameter Struct
fn ref_a (s : & StructABC.{a}) -> &u32 {
    &s.a
}

let s = StructABC {a: 4, b: 7, c: 0.0};

// partial expression at argument
let sa = ref_a(& s.{a});
let sbc = & s.{b, c};

```

And yes, for full flexibility, partial mutability is also needed:

```rust
struct S4 {a : i32, b : i32, c : i32, d : i32}

let mut.{b, c} s_mbc = S4 {a: 6, b: 7, c: 8, d: 9};

// full flexibility 
impl Point {
   pub fn mx_rstate(self : &mut.{x} Self.{x, state}) 
   { /* ... */ }

   pub fn my_rstate(self : &mut.{y} Self.{y, state}) 
   { /* ... */ }

   pub fn mxy_rstate(self : &mut.{x,y} Self.{x, y, state}) { 
    /* ... */
    self.{x, state}.mx_rstate();
    /* ... */
    self.{y, state}.my_rstate();
    /* ... */
   }
}

```

A lot of logic schemes and syntax is ready and it looks Rust-friendly.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [March 13, 2024, 5:00am UTC](https://internals.rust-lang.org/t/notes-on-partial-borrows/20020/8 "2024-03-13T05:00:49Z")

</div>

Closed on March 13, 2024, 5:00am UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
