4 ms·
LLMs do significantly better when they get reliable feedback on their actions (try to create any non-trivial project in some language without letting the LLM us
by mswphd 4mo ago
LLMs do significantly better when they get reliable feedback on their actions (try to create any non-trivial project in some language without letting the LLM use a compiler. Similarly, talking with a "chat LLM" will produce worse code than an "agentic LLM").
Anyway, making such a (breaking) change in rust immediately tells you all of the callsites that break. You have to chase it through, but that's mechanical/low context work. More formally, you can parallelize across files with sub-agents to not pollute your main agents context window. So it really should be a "zero context window" cost.
Whether or not strict typing is strictly better is really a correctness/velocity tradeoff, the same as it always has been. For most projects something in the middle is right.
As for owned vs borrowed and things propagating quite a bit, sometimes it happens. It's often avoidable with a couple of tricks
1. always default to borrowed unless you have a good reason to otherwise
2. make your function signatures more permissive so they can support either way. This can be done by modifying f<T>(x: T) (or f<T>(x: &T)) to f<U: AsRef<T>>(x: U). The later can be equivalently written as f(x: impl AsRef<T>).
When you say "change a field from owned to borrowed", I'd generally suggest not doing that. It's generally easier to start with some owned type MyType. You can then have function signatures take &MyType as input. This borrows all the fields, and is often good enough for most functions.
If you have a more esoteric function (that needs a combination of borrowed and owned inputs), it's typically easier to define a struct for that function. The steps are
1. Define a FunctionInputsRef<'a>
2. write `impl<'a> From<&'a MyStruct> for FunctionInputsRef<'a>`, then
3. update your function to take as input FunctionInputsRef<'a> rather than `&MyStruct`, and
4. update callers with `input -> input.into()`.
It has the benefit of less churn, as you're maintaining the old def (which might be useful elsewhere), and only updating the callers in a fairly trivial way. `FunctionInputsRef<'a>` can also be defined local to the function, so it is modularized better. If you later have other functions with other requirements, it's a relatively easy pattern to duplicate as well.