1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
<!DOCTYPE html>
<html>
<head>
<title>2: Solar Systems</title>
<link rel="stylesheet" href="/style.css"/>
<link rel="stylesheet" href="/prism/prism.css"/>
<script src="/prism/prism.js"></script>
</head>
<body>
<div class="window">
<div class="window-title"><h2 style="padding: 0px; margin: 0px;">Learning Rust : Solar Systems</h2></div>
<i>Everything is implicit and I don't know the exact types I'm working with.</i>
<div class="window-content">
<a class="link-button" href="/projects/learning-rust/1.html"><</a>
<a class="link-button" href="/projects/learning-rust">Home</a>
<a class="link-button" href="/projects/learning-rust/3.html">></a>
<div/>
<p>So, now that I have a struct for defining orbitals, I can begin work on a container for these orbitals. I can store the orbitals in a vector, and iterate through the list of orbitals to tick and update them for a given time. We start with the following concept code:</p>
<pre><code class="language-rust">
struct System
{
orbitals : Vec<Orbital>
}
impl System
{
pub fn tick(&self, t : Second)
{
let orbitals_len = self.orbitals.len();
for i in 0..orbitals_len {
let orbital = self.orbitals[i];
orbital.tick(self, t);
}
}
}
</code></pre>
<p>This simple code leads me to my first major hurdle when dealing with Rust: Borrows against a container take the entire object, not just the element at a given index. Because of this quirk, I cannot borrow the parent orbital to calculate parts of my orbital state vectors. Since my "tick" function needs the parent orbital to calculate the gravitational parameter. Here's the code that fuels this problem:</p>
<pre><code class="language-rust">
pub fn tick(&mut self, system: &System) -> ()
{
...
let origin_index = self.origin.unwrap();
let origin_orbital = system.orbitals[origin_index];
let origin_mass = origin_orbital.mass;
...
}
</code></pre>
<p>Because I can't borrow from the orbital vector more than once, I can't get the mass of the origin orbital.</p>
<p>Just to be clear, I understand why the borrow checker takes the entire vector when taking an element. If the element gets deleted or changed or the vector gets added to, then any other reference to the vector's elements must be re-evaluated. To properly borrow multiple mutable elements, I have to somehow split the vector in such a way that both the origin and ticking orbital are in seperate arrays.</p>
<p>Based on this information, and some help from the internet, here is what I came up with for accessing the parent-orbital pair</p>
<pre><code class="language-rust">
fn orbital_set(&self, orbitals : &mut [Orbital], i : usize)
-> (&mut Orbital, Option<&mut Orbital>)
{
//The orbital vector must first be converted to a mutable span, so that I can access the raw data.
assert!(orbitals.len() < i);
unsafe {
//From the orbital span, take a raw pointer from the given index, cast to a pointer to a mutable Orbital, and dereference.
let orbital = &mut *(orbitals.get_unchecked_mut(i) as *mut Orbital);
//Assuming the orbital is a valid object, check for the parent orbital.
match orbital.origin() {
Some(origin_i) => {
//Make sure the origin index does not break rules.
assert!(origin_i != i, "Orbital cannot orbit itself");
assert!(origin_i < orbitals.len());
let origin = &mut *(orbitals.get_unchecked_mut(i) as *mut Orbital)
(orbital, Some(origin))
},
None => (orbital, None)
}
}
}
</code></pre>
<p>I hate the unsafe block, but this is the only solution that I could find that had the characteristics that I wanted. Applied to the global tick method it looks like this:</p>
<pre><code class="language-rust">
pub fn tick(&mut self) -> ()
{
let orbitals = &mut self.orbitals.borrow_mut()[..];
for i in 0..orbitals.len() {
let orbital_set = self.orbital_set(orbitals, i);
match orbital_set.1 {
Some(origin) => { orbital_set.0.tick(origin, t); }
None => {}
}
}
}
</code></pre>
<p>where the orbital tick method looks like:</p>
<pre><code class="language-rust">
pub fn tick(&mut self, origin : &mut Orbital, t : Second)
{
...
let origin_mass = origin.mass;
...
}
</code></pre>
<p>Now the challenge will be drawing all of this information on the screen!</p>
</div>
</div>
</body>
</html>
|