ToOwned If And Only If a Value Is a Reference

I want to write a function foo, which is something like

fn foo<T>(val: T) {
    if constexpr(std::is_reference_v<T>()) {
        other_func(val.to_owned());
    } else {
        other_func(val);
    }
}

I am sure it could be done in C++ with SAFINE, but how could I do this in rustlang ?

I implemented it like this: deref_owned::GenericCow::into_owned.

Note that this requires wrapping at the caller side, as shown in this example.

Unfortunately (if I understand right), there is no idiomatic way to do it with zero-cost at runtime.

It's possible to work with the Cow type though. Perhaps that's a feasible solution for you? (Note that this also requires wrapping the value at the caller side.)


Depending on which problem you try to solve (perhaps you could share the context), you might also get away with using Borrow<T> as a bound: it is implemented both by owned types and references and allows you to obtain a reference to T. (I know this is the opposite of what you asked for, but perhaps it helps you anyway with your problem.)

Thanks for your suggestions. I was writing a some thing that turns a rust array into a js_sys::Array of JsValues.

so I wrote this.

macro_rules! jsarray {
    ( $arr: expr ) => {
        &wasm_bindgen::prelude::JsValue::from(
            $arr.map(|x| Into::<JsValue>::into(x.to_owned()))     // <-----
                .iter()
                .collect::<js_sys::Array>(),
        )
    };
}

It behaves correct but I was wondering if the to_owned call (or clone call, indirectly) would be a little expensive in term of performance if I had already owned x.

Of what type(s) is x in your example? I guess it could be any type that implements Into<JsValue> plus references to those types?

The idiomatic approach seems to omit .to_owned() in the macro and add it at the caller side, where needed. I'm not sure about that though. It's noisy, but maybe the most idiomatic approach.

Compare this recent post, where the OP finally decided to only use the owned type.


P.S.: If you work with arrays, it might be a bit verbose, though, and require .into_iter().map(ToOwned::to_owned).collect::<Vec<_>>() instead of just .to_owned() though:

fn main() {
    let vec_with_refs = vec!["Hello", "World"];
    let _: Vec<String> = vec_with_refs.into_iter().map(ToOwned::to_owned).collect();
}

(Playground)

:slightly_frowning_face:

Side note: While there is a method Iterator::cloned (and also copied), I don't see a method Iterator::owned (which I missed in the past as well). Maybe such a method could/should be added to std?

The normal way to solve this category of problem is to define a trait for the operation you want to end up with:

trait MyIntoJsValue {
    fn into_js_value(self) -> JsValue;
}

impl<T: ToOwned> MyIntoJsValue for &T
where
    T::Owned: MyIntoJsValue,
{
    fn into_js_value(self) -> JsValue {
        self.to_owned().into_js_value()
    }
}

impl MyIntoJsValue for String {
    fn into_js_value(self) -> JsValue {
        JsValue::from(self)
    }
}

// and so on...

This avoids needing the condition ā€œis not a referenceā€ which cannot be generally expressed in current Rust, because it applies to each of the non-reference types individually. It contains no unnecessary clones. The disadvantage, of course, is that if you do have an implementation that applies to ā€œall non-referencesā€, you still have to list all the types it applies to.

In the future, this might be solvable with implementation specialization, allowing you to write two blanket implementations for MyIntoJsValue and specify that the non-reference one takes priority where they overlap (which they won't in practice). However, all solutions will probably involve traits, not a compile-time if.

4 Likes

I share the same thoughts. Therefore, I have been looking for a way to add this trait to all types. Thank you for letting me know that it is not possible.

I agree that such features may likely be implemented using traits in the future. I believe these kinds of traits would provide library developers with a powerful tool to create API with reduced syntactic noise. By the way, I was wondering if there are any related RFCs scheduled at the moment. Thank you for your insights.