Method accepting an iterator of either T or &T items

โš“ Rust    ๐Ÿ“… 2025-08-27    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 4      

surdeus

I'm trying to give the possibility to build an object from a collection of either items or item references, without too much code redundancy.

In my case, T is Copy (here, it's an usize), which allows me to use Borrow like in the code below. I'm not considering something more general with types that are only cloneable.

The code it compiles to seems identical to a method that accepts respectively only T or &T, so it's well optimized, but I'm not convinced it's the best way to do it. Or even a good way to do it.

Note: I used U below to avoid any confusion with T in the titleโ€”here, T = usize. And my real use-case is of course a little more complex than that, so there's a little more code in from.

use std::borrow::Borrow;

pub struct Node<U> {
    value: U,
    children: Vec<usize>
}

impl<U, I> From<(U, I)> for Node<U>
where I: IntoIterator<Item: Borrow<usize>>    
{
    fn from((value, children): (U, I)) -> Self {
        Node {
            value,
            children: children.into_iter().map(|c| *c.borrow()).collect()
            //                                     ^^^^^^^^^^^ is it fine?
        }
    }
}

// Examples:
pub fn make_from_ref(value: String, ch: &[usize]) -> Node<String> {
    Node::from((value, ch))
}

pub fn make_from_owned(value: String, ch: Vec<usize>) -> Node<String> {
    Node::from((value, ch))
}

In some cases, I know it would be possible to define a custom trait and implement it for both T and &T, but in my case the implementations conflict because the difference is only in Item=usize / Item=&usize. Another solution, of course, is to implement two Node methods (so with different names). Either way, it's a little sad to have two copies of almost the same code.

What do you think? Is that good or bad, and is there a better solution?

2 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed