How do I implement a newtype wrapper around borrowed values?

408 Views Asked by At

I'm trying to implement a newtype wrapper around a borrowed value, in order to implement different behavior for a trait but can't get this to conform to the borrowing rules. I prepared a simplified example below.

Note: The actual code is more complex and also needs to work with values that need .as_any().downcast_ref() first, which is why I'd like this to work with borrowed values.

use std::cmp::Ordering;

trait OrdVec {
    fn cmp(&self, i: usize, j: usize) -> Ordering;
}

// note: actual trait bound is more complex and would lead to
// "conflicting implementations" when trying to implement `OrdSlice for &[f64]`
impl<T> OrdVec for Vec<T>
where
    T: Ord,
{
    fn cmp(&self, i: usize, j: usize) -> Ordering {
        self[i].cmp(&self[j])
    }
}

#[repr(transparent)]
struct F64VecAsOrdVec<'a>(&'a Vec<f64>);

impl OrdVec for F64VecAsOrdVec<'_> {
    fn cmp(&self, i: usize, j: usize) -> Ordering {
        let a: f64 = self.0[i];
        let b: f64 = self.0[j];

        if a < b {
            return Ordering::Less;
        }
        if a > b {
            return Ordering::Greater;
        }

        unimplemented!("convert NaN to canonical form and compare bitwise")
    }
}

fn as_ord_vec<'a>(first: bool, int_vec: &'a Vec<i64>, float_vec: &'a Vec<f64>) -> &'a dyn OrdVec {
    if first {
        int_vec
    } else {
        // returns a reference to data owned by the current function
        &F64VecAsOrdVec(float_vec)

        // newtype should have same layout but this fails on calling `cmp`
        // with "SIGSEGV: invalid memory reference"
        // let ord: &F64VecAsOrdVec = unsafe { std::mem::transmute(float_vec) };
        // ord
    }
}

#[test]
fn test_int_as_ord() {
    let int_vec = vec![1, 2, 3];
    let float_vec = vec![3.0, 2.0, 1.0];
    let int_ord = as_ord_vec(true, &int_vec, &float_vec);

    assert_eq!(Ordering::Less, int_ord.cmp(0, 1));
}

#[test]
fn test_float_as_ord() {
    let int_vec = vec![1, 2, 3];
    let float_vec = vec![3.0, 2.0, 1.0];
    let float_ord = as_ord_vec(false, &int_vec, &float_vec);

    assert_eq!(Ordering::Greater, float_ord.cmp(0, 1));
}
error[E0515]: cannot return reference to temporary value
  --> src/lib.rs:43:5
   |
43 |     &F64VecAsOrdVec(vec)
   |     ^-------------------
   |     ||
   |     |temporary value created here
   |     returns a reference to data owned by the current function
0

There are 0 best solutions below