Recursive java object translated to rust

#1

I just bought the book and wanted to follow along using Rust instead of Java. However I got stuck fast and wanted to get some help.

Given the following Java class:

    public class Container {

       Container[] g;
       int n;
       double x;

       public Container() {
          g = new Container[1000];
          g[0] = this;
          n = 1;
          x = 0;
        }
    }

I wanted an implementation in Rust. So I tried some basic translation given me the following.

    #[derive(Debug, Copy, Clone)]
    pub struct Container {
        g: [Box<Container>; 1000],
        n: isize,
        x: f64,
    }

    impl Container {
        pub fn new() -> Container {
            Container {
                g: [Box::new(Container::new()); 1000],
                n: 1,
                x: 0.0,
            }
        }
    }

However this is not compiling with the following error:

error[E0204]: the trait `Copy` may not be implemented for this type
 --> src/lib.rs:2:21
  |
2 |     #[derive(Debug, Copy, Clone)]
  |                     ^^^^
3 |     pub struct Container {
4 |         g: [Box<Container>; 1000],
  |         ------------------------- this field does not implement `Copy`

And also im not sure how to do g[0] = this; in the Rust case.

Why am not not able to derive the copy trait and how can i make the g[0] = this; reference work in Rust?

Iterating over struct array and mutating values
#2

You can’t implement Copy, that is fine you will just have to clone it every time. Also, it would be better to use Box<[Container; 1000]> or Vec<Container> because all of the data is kept together, which is better for performance and you don’t pay an extra cost for doing things this way. You can’t do g[0] = this because that would be self-referential which is extremely hard to do (especially in Rust).

The way to think of Copy is that a type that implements Copy doesn’t have any resources that it owns to it. Your type does have resources that it owns (the boxed memory), so it can’t be Copy.

#3

Sorry I dont understand what you are saying? Could you show in code maybe?
Yes, Vec is better but the whole book is about refactoring and the first implementation should be as naive and bare bones as possible what is why im using a Array.

#4

Vec is pretty bare bones, the only thing unnecessary is the capacity field, which you could get rid of if you use Box<[Container; 1000]>

#5

An example would be

// Note: no Copy because `Box` doesn't implement `Copy`
#[derive(Debug, Clone)]
pub struct Container {
    // note that because Java has nullable pointers, it is necessary
    // to use `Option` to represent `null`
    // also, this isn't Box<[Option<Container>; 1000]> because
    // arrays past with a length greater than 32 don't implement any traits
    // from the standard library.
    g: Box<[Option<Container>]>,
    n: isize,
    x: f64,
}

impl Container {
    pub fn new() -> Container {
        Container {
            // changed to get rid of unconditional recursion and
            // better represent what Java is doing, also because `Container`
            // is not `Copy`, we need construct the boxed slice in other ways
            g: vec![None; 1000].into(),
            n: 1,
            x: 0.0,
        }
    }
}

Some things to note about Option, Option<Container> has the same size as Container because of null-pointer optimization, so there isn’t a cost to using it.

2 Likes
#6

Nice I love the evolution of the answer, learnt so much from it. However I will have to think about how the fact that g[0] = this is not possible. That is sort of the whole point of this structure. Maybe layer chapters shows a better way of doing it then. Thanks for the help!

#7

What you could do instead is just logically think that g[0] = this, and so every time you see an index 0, you always refer back to yourself.

fn get(&self, index: usize) -> Option<&Container> {
    if index == 0 {
        Some(self)
    } else {
        self.g[index].as_ref()
    }
}

Here’s a function that would help with that

you could also implement a setter that takes into account index 0, and then only use the getter of setter.


btw
you can do this to get code highlighting for Rust

```rust
// your code here
```
3 Likes