Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Itertools::copied #289

Closed
wants to merge 1 commit into from

Conversation

KamilaBorowska
Copy link

This is to pair with cloned. There are other pairs like that in Rust, like copy_from_slice and clone_from_slice. This allows to avoid accidentally calling clone while using cloned method, by allowing to use a specialization that only accepts Copy types.

@bluss
Copy link
Member

bluss commented Jun 13, 2018

I'd vote to leave this out, it is a bit trivial and niche.

@joshtriplett
Copy link

I'd really like to have this as well. I often find myself writing .cloned() for something that implements Copy, and I'd like to avoid accidentally doing an expensive clone if I refactor somewhere. I feel like .copied() would make my code more self-documenting, and enforce invariants I care about.

@scottmcm
Copy link
Contributor

There are other pairs like that in Rust, like copy_from_slice and clone_from_slice

That's a case where the Copy bound allows a different, probably-more-efficient implementation, though. That wouldn't be the case with an Itertools::copied.

@KamilaBorowska
Copy link
Author

KamilaBorowska commented Sep 12, 2018

Something worth noting is that cloned() doesn't really do more than map(|x| x.clone()) (other than being able to name type of an iterator struct). It's not a more-efficient implementation, considering clone method can run arbitrary code.

@rkarp
Copy link
Contributor

rkarp commented Sep 15, 2018

This can actually be implemented much more simply, though I'm not sure if deref or copied would be the best name:

pub trait IteratorDerefExt: Iterator {
    fn deref<'a, T>(self) -> Cloned<Self>
        where Self: Sized + Iterator<Item = &'a T>,
              T: 'a + Clone + Copy
    {
        self.cloned()
    }
}

impl<T> IteratorDerefExt for T
    where T: Iterator {}

And yes, I'm in favor of adding this functionality as well.

@bluss
Copy link
Member

bluss commented Nov 24, 2018

Ok this sounds good to me

  1. Can we check if this is something std wants to take before we do it?
  2. If we take it, a free function copied would be a good addition too (like cloned).

I have a related problem that still doesn't have a good solution. It's a simple one: Declare a function that takes an iterator of f32 values. Make the function generic so that it accepts both iterators of &f32 and f32 in practice. What's the best solution for this?

fn sum_the_values<I>(values: I) -> f32
    where I: IntoIteratorOf<Item=f32>  // New trait?
{
     unimplemented!()
}

The reason that .copied() is not the full answer is that the distance between sum_the_values(&v) and sum_the_values(v.iter().copied()) where v is a slice or Vec is two method calls. With a free function we are down to one function: sum_the_values(copied(&v)), it's a bit better. sum_the_values(&v) would be ideal 🙂

@joshtriplett
Copy link

joshtriplett commented Nov 24, 2018

@bluss

Can we check if this is something std wants to take before we do it?

I'm all for it; I'd happily support a patch adding .copied() to core.

I have a related problem that still doesn't have a good solution. It's a simple one: Declare a function that takes an iterator of f32 values. Make the function generic so that it accepts both iterators of &f32 and f32 in practice.

Here's one solution:

https://play.rust-lang.org/?version=beta&mode=debug&edition=2018&gist=0b1520dcda103320ca0aeb0331157f58

fn sum_the_values<B, I>(values: I) -> f32
where
    B: std::borrow::Borrow<f32>,
    I: IntoIterator<Item = B>,
{
    let mut total = 0.0;
    for x in values {
        total += x.borrow();
    }
    total
}

fn main() {
    println!(
        "{} {}",
        sum_the_values(std::iter::repeat(1.1).take(5)),
        sum_the_values(std::iter::repeat(&1.1).take(5))
    );
}

@bluss
Copy link
Member

bluss commented Dec 1, 2018

Neat. I think this (playground) is the expression I'd like, to make it easy to reuse. That is, to hide the Borrow magic behind a new trait and iterator adaptor that makes it simple to use.

@bluss
Copy link
Member

bluss commented Dec 1, 2018

@joshtriplett .cloned() is already in libcore, and I guess there's a chance we can make a proposal for .copied() go through?

@joshtriplett
Copy link

@bluss Seems entirely reasonable.

Anyone interested in writing a patch?

(Skip the copied standalone function, and just add a new iterator adapter .copied(), to make the proposal more likely to go through.)

@KamilaBorowska
Copy link
Author

I'm interested in writing a patch.

bors added a commit to rust-lang/rust that referenced this pull request Dec 26, 2018
Add unstable Iterator::copied()

Initially suggested at rust-itertools/itertools#289, however the maintainers of itertools suggested this may be better of in a standard library.

The intent of `copied` is to avoid accidentally cloning iterator elements after doing a code refactoring which causes a structure to be no longer `Copy`. This is a relatively common pattern, as it can be seen by calling `rg --pcre2 '[.]map[(][|](?:(\w+)[|] [*]\1|&(\w+)[|] \2)[)]'` on Rust main repository. Additionally, many uses of `cloned` actually want to simply `Copy`, and changing something to be no longer copyable may introduce unnoticeable performance penalty.

Also, this makes sense because the standard library includes `[T].copy_from_slice` to pair with `[T].clone_from_slice`.

This also adds `Option::copied`, because it makes sense to pair it with `Iterator::copied`. I don't think this feature is particularly important, but it makes sense to update `Option` along with `Iterator` for consistency.
@joshtriplett
Copy link

Given that nightly has this available now, I don't think itertools should add it. Given that, I think this can be closed.

spikespaz pushed a commit to spikespaz/dotwalk-rs that referenced this pull request Aug 29, 2024
Add unstable Iterator::copied()

Initially suggested at rust-itertools/itertools#289, however the maintainers of itertools suggested this may be better of in a standard library.

The intent of `copied` is to avoid accidentally cloning iterator elements after doing a code refactoring which causes a structure to be no longer `Copy`. This is a relatively common pattern, as it can be seen by calling `rg --pcre2 '[.]map[(][|](?:(\w+)[|] [*]\1|&(\w+)[|] \2)[)]'` on Rust main repository. Additionally, many uses of `cloned` actually want to simply `Copy`, and changing something to be no longer copyable may introduce unnoticeable performance penalty.

Also, this makes sense because the standard library includes `[T].copy_from_slice` to pair with `[T].clone_from_slice`.

This also adds `Option::copied`, because it makes sense to pair it with `Iterator::copied`. I don't think this feature is particularly important, but it makes sense to update `Option` along with `Iterator` for consistency.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants