ITADN

Unable to call `SectionSetter::set` again due to too-restrictive lifetimes on method signature

#134Closedrikyborg 创建于 2024-08-01
R
rikyborgcommented
Once a `SectionSetter` is obtained by e.g. calling `Ini::with_section()`, it's possible to set multiple key-value pairs by chaining calls to `SectionSetter::set()`. However, it is not possible to call `set()` multiple times on the same variable of type `SectionSetter`. The cause seems to be too restrictive lifetimes in the signature of `SectionSetter::set()`. ### Example The following code: ```rust use ini::Ini; fn main() { let mut ini = Ini::new(); let mut section_setter = ini.with_section(Some("section")); // chaining set() calls work section_setter.set("a", "b").set("c", "d"); // but calling set() separately doesn't work section_setter.set("e", "f"); // <--- error[E0499] } ``` fails to compile with the output ``` error[E0499]: cannot borrow `section_setter` as mutable more than once at a time --> src/main.rs:10:5 | 8 | section_setter.set("a", "b").set("c", "d"); | -------------- first mutable borrow occurs here 9 | // but calling set() separately doesn't work 10 | section_setter.set("e", "f"); | ^^^^^^^^^^^^^^ | | | second mutable borrow occurs here | first borrow later used here For more information about this error, try `rustc --explain E0499`. error: could not compile `inisec` (bin "inisec") due to 1 previous error ``` using `rust-ini v0.21.0` and `rustc v1.80.0`. ### Probable cause The signature of `SectionSetter::set()` is ```rust pub fn set<K, V>(&'a mut self, key: K, value: V) -> &'a mut SectionSetter<'a> where K: Into<String>, V: Into<String>, {...} ``` where `'a` is the lifetime of the `Ini` instance borrowed by `SectionSetter<'a>`. This signature requires `set()` to borrow `self: SectionSetter` for `'a`, which is the whole duration of the original borrow of `Ini`. So once `set()` is called on the variable `section_setter`, that variable stays borrowed for its whole lifetime and it's impossible to call `set()` again on it. ### Possible solution Changing the signature to: ```rust pub fn set<'b, K, V>(&'b mut self, key: K, value: V) -> &'b mut SectionSetter<'a> where K: Into<String>, V: Into<String>, 'a: 'b, {...} ``` makes the example code above compile (and all `rust-ini` tests still pass). What we do here is introduce a new lifetime `'b`, and make the new borrow of `SecionSetter<'a>` only last for `'b`. We also require that `'a` is larger than `'b` with the bound `'a: 'b`. This way, once the chain of `set()` calls on the variable `section_setter` ends, `'b` expires and the original variable `section_setter` is not borrowed anymore and we can explicitly call `set()` again. ### Other methods I don't have a good-enough grasp of the whole API surface of `rust-ini` to judge whether this change should be applied to other methods as well. Which is why I'm filing this as an issue rather than a PR. Most probably this applies to `add()`, `delete()` and `get()` as well.
关闭于 2024-08-02 2 条评论