Skip to content

View type #343

Description

@arialpew

Borrowck doesn't support partial borrow across regular function boundary.

View type aim to solve this problem.

With a classical approach where struct fields are private, Borrowck fail to reason about partial borrow across regular function boundary. If you start to write getters, you'll likely encounter a situation where you want to borrow disjoint fields of your struct across function call. You can't : a single getter lock the entiere memory region of the struct for Borrowck.

View type encapsulate another type and act as a readonly view. This is so far the cleanest and most ergonomic solution, as shown in the example below. As all fields are now public, Borrowck is able to figure out that we're actually borrowing disjoint fields.

The only inconvenience is that you need to define the ReadOnly structure in each module. This is because you want get_mut to only be accessible by the structure that owns ReadOnly (in other words, get_mut can't be public).

This pattern is already used in numerous code base.

#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]

use std::ops::Deref;

struct ReadOnly<T> {
    data: T,
}

impl<T> ReadOnly<T> {
    pub fn new(data: T) -> Self {
        ReadOnly { data }
    }
    
    pub fn get(&self) -> &T {
        &self.data
    }

    // Private.
    fn get_mut(&mut self) -> &mut T {
        &mut self.data
    }
}

impl<T> Deref for ReadOnly<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

struct Foo {
    pub a: ReadOnly<Vec<i32>>,  // Public read-only  field.
    pub b: Vec<f32>,            // Public read-write field.
    pub c: Vec<i32>,            // Public read-write field.
    // ... maybe more fields ...
    pub z: Vec<bool>,           // Public read-write field.
}

impl Foo {
    pub fn new() -> Self {
        Self {
            a: ReadOnly::new(vec![1, 2, 3]),
            b: vec![1.0, 2.0, 3.0],
            c: vec![-3, 0, 3],
            z: vec![false, true],
        }
    }
}

pub fn main() {
    let mut foo = Foo::new();

    {   // This now works.
        let x     = foo.a.get();  // Immutably borrow `a`.
        let mut y = &mut foo.b;   // Mutably borrow `b`.
        for i in x { }            // Immutably use `a`.   
    }
    
    {   // This is now erroneous.
        let mut x = &mut foo.a;    // Can still borrow ReadOnly as mutable.
        let mut y = &mut foo.b;    // Mutably borrow `b`.
        for i in x.iter_mut() { }  // Can't use `a` as mutable.
    }
}

Activity

  1. locked and limited conversation to collaborators on Feb 16, 2023
  2. converted this issue into a discussion #344 on Feb 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions