Reputation: 4468
I'm just learning Rust, coming from years of C++, and I'm having some difficulty understanding how to deal with some of the ownership restrictions. Specifically, in this little program, which is just a training exercise, I get two different error messages.
let stat = Stdev::new();
The above statement gets the message: move occurs because stat
has type Stdev
, which does not implement the Copy
trait
stat.add(x);
The above statement gets the message: stat
moved due to this method call, in previous iteration of loop.
Here's the whole program:
struct Stdev
{
count : u32,
sum : f64,
sq_sum : f64
}
impl Stdev
{
fn new() -> Self
{
Stdev{count:0, sum:0.0, sq_sum:0.0}
}
fn add(mut self, x : f64)
{
self.count += 1;
self.sum += x;
self.sq_sum += x * x;
}
fn stdev(&self) -> f64
{
if self.count == 0 { return 0.0; }
let n = self.count as f64;
let mean = self.sum / n;
let variance = self.sq_sum / n - mean * mean;
variance.sqrt()
}
}
use rand::Rng;
fn main() {
let count = 1000;
let stat = Stdev::new();
for _ in 1..count
{
let x = rng.gen_range(0.0..100.0);
stat.add(x);
}
println!("Standard deviation is {}", stat.stdev());
}
So, two questions:
1: How can I initialize the variable stat
with an instance of the Stdev
struct?
2: How can I modify fields within the struct via a method call without running into ownership problems?
Just as a reference, here is essentially the same logic, in C++ instead of Rust, and this program works just fine. In the following code, the class accum
is the equivalent of the Rust struct Stdev
.
#include <iostream>
#include <cmath>
class accum
{
int count;
double sum;
double sq_sum;
public:
accum();
int add(double);
double stdev();
};
accum::accum()
{
count = 0;
sum = 0.0;
sq_sum = 0.0;
}
int accum::add(double x)
{
sum += x;
sq_sum += x * x;
return ++count;
}
double accum::stdev()
{
if (0 == count) return 0;
double mean = sum / count;
double variance = sq_sum / count - mean * mean;
return sqrt(variance);
}
double gen_range()
{
double x = rand();
x /= RAND_MAX;
return x * 100.0;
}
int main()
{
int count = 100;
accum stat;
for(int i = 0; count > i; ++i)
{
double x = gen_range();
stat.add(x);
}
std::cout << "Standard deviation is " << stat.stdev() << "\n";
}
I'm particularly confused about the second error. If stat
was moved, where was it moved to? There was no assignment, just a method call. I'm confused. Thanks.
Upvotes: 0
Views: 102
Reputation: 1
You can use Default::default()
to initialize.
Comparing ownership transfers in rust and C++
https://www.reddit.com/r/rust/comments/du87ou/comparing_ownership_transfers_in_rust_and_c/
Whereas C and C++ only let you apply the & operator to certain kinds of expressions, Rust lets you borrow a reference to the value of any sort of expression at all. Programming Rust: Fast, Safe Systems Development 2nd Edition, Kindle Edition by Jim Blandy (Author), Jason Orendorff (Author), Leonora F. S. Tindall (Author) _Borrowing References to Arbitrary Expressions.
struct Stdev {
count: u32,
sum: f64,
sq_sum: f64,
}
impl Stdev {
fn new() -> Self {
Stdev {
count: Default::default(),
sum: Default::default(),
sq_sum: Default::default(),
}
}
fn add(&mut self, x: f64) {
self.count += 1;
self.sum += x;
self.sq_sum += x * x;
}
fn stdev(&self) -> f64 {
if self.count == 0 {
return 0.0;
}
let n = self.count as f64;
let mean = self.sum / n;
let variance = self.sq_sum / n - mean * mean;
variance.sqrt()
}
}
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let count = 1000;
let mut stat = Stdev::new();
for _ in 1..count {
let x = rng.gen_range(0.0..100.0);
stat.add(x);
}
println!("Standard deviation is {}", stat.stdev());
}
Standard deviation is 29.224928446158625
Upvotes: -1
Reputation: 6584
Change the add
function to take a mutable reference to self
instead of moving it:
fn add(&mut self, x: f64)
Using the current declaration, calling add
will actually move stat
and pass the ownership to the add
method. Hence, stat
can not be used after that call, but the loop needs it, this is why you get the error.
Your new
method is fine.
Upvotes: 1