Feeling Rust is so difficult

I am trying to filter a collection into a sub collection so that the sub collection can be handed off to another fn to do some other calculations.

The problem is fn foo. Tried different ways but just cannot seem to get it to compile.

Thinking is it because my approach is wrong? How would you code such a requirement in Rust?

use std::cmp::PartialEq;

fn foo(mut elems: Vec<Foo>) {
    baz(&mut elems);

    let classes = vec!( AssetClass{name: String::from("A")},AssetClass{name: String::from("B")});

    for class in classes {
        let some: Vec<Foo> = &elems.into_iter().filter(|e| e.id == class).collect();
        some == true;
        bar(some);
    }

    println!("")
}

struct Foo {
    id: AssetClass
} 

#[derive(PartialEq)]
struct AssetClass {
    name: String
}

fn bar(elems: Vec<Foo>) {

}

fn baz(mut elems: &Vec<Foo>) {

}

fn xxx(mut elems: &Vec<Foo>) {

}

fn main() {

}

A few problems are present here:

  • Line 9: you’re assigning a reference to a non-reference. You’re saying let _: T = &T which is invalid, as though you would need to move out of a reference where T: !Copy. You may want let _: T = T.
  • Line 9: you’re calling elems.into_iter(), which takes elems by value. Meaning that it is moved into the function and is no longer available for the next iteration. Therefore this breaks.
  • Line 10: What are you trying to say by saying some == true;
  • As far as I understand you want to filter the input (elems) given some predicates (classes). I’d recommend something like the following:
fn foo(elems: Vec<Foo>) -> Vec<Foo> {
    let classes = vec![ ... ];
    let new_elems: Vec<Foo> = elems.into_iter().filter(|x| classes.contains(&x.id)).collect();
    new_elems
}
  • Or perhaps if you want to clone all of those that satisfy the condition:
fn foo(elems: Vec<Foo>) -> Vec<Foo> {
    let classes = vec![ ... ];
    let new_elems = Vec<Foo> = elems.iter().filter(|x| classes.contains(&x.id)).cloned().collect();
    new_elems
}

Thank you so much for taking time to guide me on this.

Line 10: Sorry i didn’t clean this up first. I was using this to see the type of some. Do let me know if there is a better way of knowing type of variable.

Line 9. I see what you mean. So this means your suggested solution 1 is not going to work here yah? The only possible solution is to do a clone. Was trying to avoid that because i was told copying should be last resort.

Line 9. I tried to use you suggested solution 2. (with modifications)

let some: Vec<Foo> = elems.iter().filter(|x| classes.contains(&x.id)).map(|x| x.deref()).clone().collect();

But i get this very familiar error.

let some: Vec<Foo> = elems.iter().filter(|x| classes.contains(&x.id)).map(|x| x.deref()).clone().collect();
                                                                                                 ^^^^^^^ a collection of type `std::vec::Vec<Foo>` cannot be built from `std::iter::Iterator<Item=&Foo>`

I thought by doing deref i can get to the actual object Foo and not the reference and then clone that.

You have to use cloned and not clone like in @OptimisticPeach’s reply.
clone will try to clone the iterator while cloned will clone each element yielded by it.

3 Likes

Yes, expanding on @leudz’s post, it’s similar to saying the following:

iter.map(|x| x.clone())
//Same as
iter.cloned()

Or, with the recent stabilizations

iter.map(|x| *x)
//Same as
iter.copied()
2 Likes

Oh my, this is new info to me. I thought it was a typo at first. I have considered the clone approach before. However I hit a roadblock with that when Foo has an attribute that is a trait object. And it seems that trait object cannot be cloned

1 Like

The trait object cannot be cloned unless the trait object says so. But even then you cannot use Clone directly, or even a trait Foo: Clone because its signature is

fn clone(&self) -> Self

but Self is unsized, and therefore cannot be passed around like this function wants it to be.

So therefore you’d need a new trait that defines cloning a ?Sized object:

pub trait BoxClone: ?Sized + Clone {
    fn clone_boxed(&self) -> Box<Self>;
}

And implement it like so:

impl<T: Clone> BoxClone for T {
    fn clone_boxed(&self) -> Box<Self> {
        Box::new(self.clone())
    }
}

Really feels like something that can be done effortlessly in other language need a lot of work to achieve in rust

Perhaps in some cases, but the ability to explain things in more detail and not need to worry about certain things being “magic-ed away” by a garbage collector is a pretty important thing. And also, have you ever tried to write c/c++ code, I tried and it was a nightmare…

3 Likes

But I know a lot of places where it is the other way around, example: Iterate over an array, and sum all even digits

fn sum_even(a: &[u32]) -> u32 {
    a.iter().filter(|x| x % 2 == 0).sum()
}

easy pie. There are even more advances things which are so easy with iterators, it’s amazing. Image the amount of time you have to spend in C or C++. Manually iterating, making sure to not access it out of bounds. :sleepy:

:crab: is :heart:

3 Likes

Iterating over literals is simple enough. It’s iterating over objects that really confuses me. into_iter, iter, iter_mut… Wanting to return a vector of references, or actual objects. When you call foo.iter the foo is borrowed or not…and so on…

into_iter is a trait method from the IntoIterator trait. Use that, when you want to iterate over the values itself (instead of references). You have to give up ownership of course for that.

iter is like into_iter, but without giving up ownership and therefore you can only iterate over references of the values of your collection.

iter_mut is like iter, but lets you modify the values of the underlying iterator. You need mutable ownership/mutable reference of the underlying object of course.

What you actually want depends on your context. Fast and simple is into_iter. No allocation and no references, just pure object. iter when you need the object later on or just have a reference. iter_mut if you want to modify them. Easy as :cake:

2 Likes

Thank you for the detailed explanation. In Rust, is it recommended practice to work with reference always (not cloning) since this way there won’t be memory deallocation and allocation overheads. I have read some articles which recommends not to keep objects for too long and minimise context for which objects live so that ownership is easier to manage.

For example, i have a collection that needs to go through several calculations, filtering, mapping, logics and stuff. I can in theory just keep working using reference and mutating memory. Wonder if that is good practice (i am a functional programming enthusiast and cringe at the thought of mutation)

1 Like

I realize I’m taking you slightly out of context, but I feel this is at odds with

If you want the effortless feel of other languages, you need to at least adopt their way of modeling the problem. All those “other languages” are doing, most of the time, is making a sneaky clone while your back is turned. For example, most functional languages use some form of garbage collection, so the programmer doesn’t have to think about object lifetimes; if you want the same in Rust, it’s reasonable to use Rc, which can contain trait objects and is cheap to clone.

Just because Rust allows you to write super cool non-allocating zero-copy algorithms safely, doesn’t mean every algorithm you write should be super cool, zero-copy and non-allocating.

3 Likes

If you’re trying to do what you would do in a memory-managed language, you probably want to use Rc<dyn Trait> rather than Box<dyn Trait>; this most closely preserves the semantics of having multiple owned references that point to the same object. (and it always implements Clone)

Rc will forbid mutation; if you further need to mutate the object, use Rc<RefCell<dyn Trait>>.

1 Like

Coming from FP, this bothered me to begin with too - let me paraphrase a quote I heard that finally made it click for me:

Most people will agree that the biggest source of confusion and bugs in software is shared mutable state.

Functional languages solve this by making the mutable stuff immutable. Rust solves this by making the mutable stuff not sharable. They’re both trying to solve the same problem, just in different ways :slight_smile:

17 Likes

Fortunately none of those operations (filtering, mapping, etc) would require mutation in rust, so you’re good there!

1 Like

Actually I do, cause it’s a collection of objects and I want to mutate the variables of the object

Okay, but that is a design choice (probably a good one) unrelated to the mapping, filtering and other operations that one does with iterators.

1 Like

Actually I do, cause it’s a collection of objects and I want to mutate the variables of the object

Getting to know the difference between mut and other types are very important in the beginning as this concept can be found in multiple places such as iter() and iter_mut() and into_iter(), as well as get() and get_mut(). One nice periodic table for rust types http://cosmic.mearie.org/2014/01/periodic-table-of-rust-types/

Having heard the mention of word object quite a few times, I would say that getting to think from a data point of view is very important in rust, which might be a bit different as compared to thinking from an object perspective. Rust Koans​​​​​ describes this nicely in story form.

1 Like