Idiomatic way to implement identity function for borrowed argument


#1

Hi Rustaceans,
I’m just started learning Rust so probably do not know right keywords to search my question, so I’m sorry if this question already was answered.

As my first excercise I decided to implement program, that prints table of function values for given arguments. And I came up 2 problems for me:

  1. I would like to work with arbitrary integers;
  2. I would like to have identity function.

To solve this problem I declared this struct:

struct MyFun<'a> {
    name: &'a str,
    body: fn(&BigInt) -> ???,
}

And I have found 4 ways to declare return type of body function:

  • BigInt - this way I like the most, but I do not like that I should clone argument in identity function:
fn n(x: &BigInt) -> BigInt {
    return x.clone();
}
  • Cow<BigInt> - I think it’s the most efficient working way, but looks cumbersome for me
  • &BigInt - I can not find way to implement other functions in this way:
fn nn(x: &BigInt) -> &BigInt {
    let res = x * x;
    return &res; // error: `res` does not live long enough
}
  • Box<BigInt> - Will solve lifetime problem in previous way, but again I need to clone argument in identity function and it looks cumbersome

So what is idiomatic way in Rust to implement those two functions?

Thanks.

Full code:

extern crate num;

use num::FromPrimitive;
use num::bigint::*;

use std::borrow::Cow;

struct MyFun<'a> {
    name: &'a str,
    body: fn(&BigInt) -> Cow<BigInt>,
}

fn n(x: &BigInt) -> Cow<BigInt> {
    return Cow::Borrowed(x);
}

fn nn(x: &BigInt) -> Cow<BigInt> {
    let res = x * x;
    return Cow::Owned(res);
}

fn main() {
    let functions = [
        MyFun {
            name: "n",
            body: n,
        },
        MyFun {
            name: "nn",
            body: nn,
        }];

    let second: BigInt = FromPrimitive::from_u64(1).unwrap();
    let minute: BigInt = &second * &(FromPrimitive::from_u64(60).unwrap());
    let hour = &minute * &(FromPrimitive::from_u64(60).unwrap());
    let day = &hour * &(FromPrimitive::from_u64(24).unwrap());
    let month = &day * &(FromPrimitive::from_u64(30).unwrap());
    let year = &month * &(FromPrimitive::from_u64(12).unwrap());
    let century = &year * &(FromPrimitive::from_u64(100).unwrap());
    let arguments = [second, minute, hour, day, month, year, century];

    for f in functions.iter() {
        print!("{}: ", f.name);
        for a in arguments.iter() {
            print!("{} ", (f.body)(a));
        }
        println!("");
    }
}

#2

If:

  • the returned value is sometimes borrowed and sometimes owned, and
  • the integers are big enough that you have measured cloning them to be a bottleneck in your application, and
  • the performance of your application matters to you,

… then go with Cow<BigInt>. Otherwise go with BigInt.


You enumerated almost all the options I would think of. Just one more to add:

use std::fmt::Display;

struct MyFun<'a> {
    name: &'a str,
    body: for<'i> fn(&'i BigInt) -> Box<Display + 'i>,
}

fn n<'i>(x: &'i BigInt) -> Box<Display + 'i> {
    Box::new(x)
}

fn nn(x: &BigInt) -> Box<Display> {
    let res = x * x;
    Box::new(res)
}

fn main() { /* is the same */ }

Advantages:

  • No cloning
  • Functions can return primitives directly like Box::new(0), don’t need to wrap in BigInt

Disadvantages:

  • More memory allocations than Cow
  • You need to enumerate everything you intend to do with the return type (in this case Display them)

Probably doesn’t make sense in this case but it can be really useful in other similar cases.


#3

Thanks!