fieldwork-borrow 0.1.0

Field-level borrow splitting for Rust
Documentation
//! e1: closure that calls &mut self method (get_or_insert_with)

use fieldwork_borrow::with_;

struct SomeState(u32);

fn new_state(x: u32) -> SomeState {
    SomeState(x)
}

struct StorageEngine {
    page: u32,
    state: Option<SomeState>,
    c: u32,
}

impl StorageEngine {
    // Original problem code (won't compile):
    // fn problem(&mut self) -> (&mut u32, &mut u32, &mut SomeState) {
    //     let state = self.state.get_or_insert_with(|| {
    //         let x = self.assign_next_page();
    //         new_state(x)
    //     });
    //     (&mut self.c, &mut self.page, state)
    // }

    fn problem(&mut self) -> (&mut u32, &mut u32, &mut SomeState) {
        with_!(self, {
            methods {
                assign_next_page(mut page) -> u32 {
                    let p = *page;
                    *page += 1;
                    p
                }
            }
            fields {
                mut state
                mut c
            }
            body {
                let state_ref = state.get_or_insert_with(|| {
                    let x = self.assign_next_page();
                    new_state(x)
                });
                (c, page, state_ref)
            }
        })
    }

    // Original method for reference
    fn _assign_next_page(&mut self) -> u32 {
        let page = self.page;
        self.page += 1;
        page
    }
}

#[test]
fn test_e1() {
    let mut engine = StorageEngine {
        page: 10,
        state: None,
        c: 100,
    };

    let (c, page, state) = engine.problem();

    // assign_next_page should have been called, incrementing page from 10 to 11
    assert_eq!(*page, 11);
    // state should be initialized with value 10 (the old page value)
    assert_eq!(state.0, 10);
    // c should be accessible
    assert_eq!(*c, 100);
}

#[test]
fn test_e1_already_initialized() {
    let mut engine = StorageEngine {
        page: 10,
        state: Some(SomeState(999)),
        c: 100,
    };

    let (_, page, state) = engine.problem();

    // state was already initialized, so assign_next_page should NOT be called
    assert_eq!(*page, 10);  // page unchanged
    assert_eq!(state.0, 999);  // original state preserved
}