1

I have a function which takes a reference and a condition. If the condition is false, it uses the passed in reference, but when the condition is true, it use a temp reference pointing to the local variable in the function.

The Foo struct is NOT Copy or Clone.

The commented-off code won't compile and I have an ugly way to work around this problem:

#[derive(Debug)]
struct Foo {
    a: String,
}

fn f(r: &Foo, cond: bool) {
    // let a = if cond { &Foo { a: "456".into() } } else { r };

    // use a temp variable `t` can solve this problem in this demo code,
    // but what if in production code where build tmp var `t` is expensive?
    let mut a = r;
    let t = &Foo { a: "456".into() };
    if cond {
        a = &t;
    }

    // use the ref here, inside the function.
    println!("{:#?}", a);
}

fn main() {
    let a = Foo { a: "123".into() };
    f(&a, true);
}

Because the use of the reference is limited in the function, is there a way to tell the compiler: do not free the temp var created in the if branch, keep it until the function return and destroy it when destroy the stack frame?

I think there should be a way without using heap memory. On the other hand, when using Box, the type produced in the two if branch will be different, one is &Foo and another is Box<Foo>, so this won't be the right way.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Myrfy
  • 575
  • 4
  • 11

1 Answers1

0

Cow to the rescue! Cow is an enum that represents either (1) a thing that we own or (2) a thing we have borrowed.

#[derive(Debug, Clone)]
struct Foo {
    a: String,
}

fn f(r: &Foo, cond: bool) {
    use std::borrow::Cow;

    let a: Cow<Foo> = if cond {
        // (1) a thing that we own, or
        Cow::Owned(Foo { a: "456".into() })
    } else {
        // (2) a thing we have borrowed
        Cow::Borrowed(r)
    };

    // use the ref here, inside the function.
    println!("{:#?}", a);

    // if a is a Cow::Owned, the owned thing is dropped properly
    // if a is a Cow::Borrowed, the reference is dropped.
}

fn main() {
    let a = Foo { a: "123".into() };
    f(&a, true);
}

Cow requires Clone, however, which is a little bit surplus to what we already have, but it's not a terribly onerous burden.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
NovaDenizen
  • 5,089
  • 14
  • 28