# fieldwork-borrow

Field-level borrow splitting for Rust.

Rust's borrow checker operates at the function boundary level, which sometimes rejects code that is actually safe. `fieldwork-borrow` provides a `with_!` macro that enables field-level borrow splitting, allowing you to work around these "false positive" borrow checker errors.

## The Problem

Consider this common pattern that Rust rejects:

```rust
struct Viewer {
    db: Database,
    model_id: usize,
    object_mats: Vec<f64>,
}

impl Viewer {
    fn cur_model(&self) -> &Model {
        &self.db.models[self.model_id]
    }

    fn problem(&mut self) {
        // ERROR: cannot borrow `self.object_mats` as mutable because
        // `self` is also borrowed as immutable (by cur_model)
        for obj in &self.cur_model().objects {
            self.object_mats.push(obj.matrix);
        }
    }
}
```

The code is safe because `cur_model()` only reads `db` and `model_id`, while we're mutating `object_mats`. But Rust can't see through the method boundary.

## The Solution

```rust
use fieldwork_borrow::with_;

impl Viewer {
    fn problem(&mut self) {
        with_!(self, {
            methods {
                cur_model(&db, copy model_id) -> &Model {
                    &db.models[model_id]
                }
            }
            fields {
                mut object_mats
            }
            body {
                for obj in &self.cur_model().objects {
                    object_mats.push(obj.matrix);
                }
            }
        });
    }
}
```

The `with_!` macro:
1. Extracts fields into local variables with precise borrowing
2. Inlines method bodies at call sites
3. Transforms `self.field` accesses to use the local variables

## Syntax

```rust
with_!(self, {
    methods {
        method_name(field1, mut field2, copy field3, extra_arg: Type) -> ReturnType {
            // method body - field1/field2/field3 are available here
        }
    }
    fields {
        field_a           // immutable borrow (&self.field_a)
        mut field_b       // mutable borrow (&mut self.field_b)
    }
    body {
        // your code here
        // - self.method_name(...) calls are inlined
        // - self.field_a / self.field_b access the local variables
        // - field_a / field_b also work directly
    }
});
```

### Field Access Modes

In method signatures:
- `field` or `&field` - immutable borrow
- `mut field` - mutable borrow
- `copy field` - copy by value (for `Copy` types)

In `fields` block:
- `field` - immutable borrow
- `mut field` - mutable borrow

## Examples

### Closure with `get_or_insert_with`

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

### Iterator with method call

```rust
fn problem(&mut self) {
    with_!(self, {
        methods {
            append_slice(mut slices, val: u64, idx: usize) {
                slices.some_code();
            }
        }
        fields { segments }
        body {
            for (i, s) in segments.iter().enumerate() {
                self.append_slice(some_f(s), i);
            }
        }
    });
}
```

### Multiple methods accessing shared state

```rust
fn problem(&mut self) {
    with_!(self, {
        methods {
            cur_model(&db, copy model_id) -> &Model {
                &db.models[model_id]
            }
            cur_animation(&db, &conn, copy model_id) -> Option<&Animation> {
                conn.models[model_id].animations.get(0)
                    .map(|&id| &db.animations[id])
            }
        }
        fields { mut uv_mats, mat_anim_state }
        body {
            if let Some(anim) = self.cur_animation() {
                for (i, _) in self.cur_model().materials.iter().enumerate() {
                    uv_mats[i] = anim.tracks[0].eval(mat_anim_state.frame);
                }
            }
        }
    });
}
```

## Limitations

- Methods defined in `with_!` are inlined at call sites, so they cannot be recursive
- For recursive patterns, extract the recursive logic into a standalone function that takes the split fields as parameters
- The `?` operator in method bodies may need adjustment since the method is inlined

## Installation

```toml
[dependencies]
fieldwork-borrow = "0.1"
```

## License

MIT
