在每个循环迭代中放弃借用。

huangapple go评论58阅读模式
英文:

Drop borrow in each loop iteration

问题

Here's the translated code without the parts you mentioned:

struct Val<'a>(&'a str);

impl Val<'_> {
    fn get(&self) -> Option<&str> {
        Some(self.0)
    }
}

fn test<'a>(param: &'a mut Val<'a>) -> Option<&'a str> {
    param.get()
}

fn main() {
    let s = String::from("hello");
    let mut v = Val(&s);

    while let Some(x) = test(&mut v) {
        println!("{}", x);
    }
}

Please note that I have removed the HTML character escapes like &amp; and &quot; to provide you with clean Rust code.

英文:

This is a reproducible simplification of my problem:

struct Val&lt;&#39;a&gt;(&amp;&#39;a str);

impl Val&lt;&#39;_&gt; {
    fn get(&amp;self) -&gt; Option&lt;&amp;str&gt; {
        Some(self.0)
    }
}

// Not used here, but this borrow needs to be mutable
fn test&lt;&#39;a&gt;(param: &amp;&#39;a mut Val&lt;&#39;a&gt;) -&gt; Option&lt;&amp;&#39;a str&gt; {
    param.get()
}

fn main() {
    let s = String::from(&quot;hello&quot;);
    let mut v = Val(&amp;s);
    
    while let Some(x) = test(&amp;mut v) {
        println!(&quot;{x}&quot;);
    }
}

playground link

I get:

   |
17 |     while let Some(x) = test(&amp;mut v) {
   |                              ^^^^^^
   |                              |
   |                              `v` was mutably borrowed here in the previous iteration of the loop
   |                              first borrow used here, in later iteration of loop

For more information about this error, try `rustc --explain E0499`.

First I tried to change the lifetime of the borrow, to indicate that it does not live as long as the original data, like this:

fn test&lt;&#39;a, &#39;b&gt;(param: &amp;&#39;b mut Val&lt;&#39;a&gt;) -&gt; Option&lt;&amp;&#39;a str&gt; {
    param.get()
}

But then I get:

   |
9  | fn test&lt;&#39;a, &#39;b&gt;(param: &amp;&#39;b mut Val&lt;&#39;a&gt;) -&gt; Option&lt;&amp;&#39;a str&gt; {
   |         --  -- lifetime `&#39;b` defined here
   |         |
   |         lifetime `&#39;a` defined here
10 |     param.get()
   |     ^^^^^^^^^^^ function was supposed to return data with lifetime `&#39;a` but it is returning data with lifetime `&#39;b`
   |
   = help: consider adding the following bound: `&#39;b: &#39;a`

Adding the suggested bound brings me back to the previous error.

I checked this related question, which suggests this is a limitation of the borrow checker, so I tried changing my code to:

fn main() {
    let s = String::from(&quot;hello&quot;);
    let mut v = Val(&amp;s);
    
    loop {
        let x = test(&amp;mut v);
        if let Some(y) = x {
            println!(&quot;{y}&quot;);
        }
    }
}

playground link

But I get the same first error. In my use case test behaves more or less like a next from iterator, so I can't call it two times in the same iteration.

Is there any way to:

  • return data with lifetime &#39;a instead of &#39;b in fn test?
  • or, with the single &#39;a lifetime, make the borrow be dropped at the end of the iteration?

Edit: When translating back to my real problem, I noticed that this example does not completely represent it, and I still had issues making it work. I did some changes to the example here with some additional changes in lifetimes to make it work. Now it works fine. In summary, the test function had to do some mapping on the value returned by get, and in that process it was returning lifetime &#39;b instead of &#39;a.

答案1

得分: 2

The inherent function Val::get() has the wrong lifetime in the return value. The function returns the inner value of Val, so the return value should have the lifetime of that inner value. Due to lifetime elision, the signature of your implementation of get() is equivalent to

fn get<'b>(&'b self) -> Option<&'b str>;

In other words, self will always be borrowed for the entire lifetime of the returned value, which is unnecessary. The lifetime of the borrow of self is completely independent of the lifetime of the returned string.

You also need to decouple the two lifetimes in test(), as you already tried. Here's the full working code:

struct Val<'a>(&'a str);
    
impl<'a> Val<'a> {
    fn get(&self) -> Option<&'a str> {
        Some(self.0)
    }
}

fn test<'a, 'b>(param: &'b mut Val<'a>) -> Option<&'a str> {
    param.get()
}

fn main() {
    let s = String::from("hello");
    let mut v = Val(&s);

    while let Some(x) = test(&mut v) {
        println!("{}", x);
    }
}

(I added 'b for clarity in test(). You could also simply omit it and use &mut Val<'a> as the type of param, again due to lifetime elision.)

英文:

The inherent function Val::get() has the wrong lifetime in the return value. The function returns the inner value of Val, so the return value should have the lifetime of that inner value. Due to lifetime elision, the signature of your implementation of get() is equivalent to

fn get&lt;&#39;b&gt;(&amp;&#39;b self) -&gt; Option&lt;&amp;&#39;b str&gt;;

In other words, self will always be borrowed for the entire lifetime of the returned value, which is unnecessary. The lifetime of the borrow of self is completely independent of the lifetime of the returned string.

You also need to decouple the two lifetimes in test(), as you already tried. Here's the full working code:

struct Val&lt;&#39;a&gt;(&amp;&#39;a str);

impl&lt;&#39;a&gt; Val&lt;&#39;a&gt; {
    fn get(&amp;self) -&gt; Option&lt;&amp;&#39;a str&gt; {
        Some(self.0)
    }
}

fn test&lt;&#39;a, &#39;b&gt;(param: &amp;&#39;b mut Val&lt;&#39;a&gt;) -&gt; Option&lt;&amp;&#39;a str&gt; {
    param.get()
}

fn main() {
    let s = String::from(&quot;hello&quot;);
    let mut v = Val(&amp;s);

    while let Some(x) = test(&amp;mut v) {
        println!(&quot;{x}&quot;);
    }
}

(I added &#39;b for clarity in test(). You could also simply omit it and use &amp;mut Val&lt;&#39;a&gt; as the type of param, again due to lifetime elision.)

huangapple
  • 本文由 发表于 2023年4月17日 17:36:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76033693.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定