How to fix borrowing of moved value

This is my little beginner code and I do not know how to fix it.

use rug::{ Integer, Rational };

fn main() {
	const N: usize = 20;
	let mut c: [Rational; N];
	let mut nom : Integer;
	let mut denom : Integer;
	
	denom = Integer::from(1);
		
	for k in 1..N+1 {
		denom *= Integer::from(2*k*(2*k+1));
		let mut num = Rational::from((k, denom));
		let mut den = Integer::from(1);
		
		for i in 1..k {
			den *= (2 * i * (2 * i + 1)) as u32;
			num -= c[k - i - 1] / den;
		}
		
		c[k - 1] = num;
		println!("{}", num);
	}
}

What's the error? rug is unavailable on playground, so we can't reproduce it ourselves.

At the line

denom *= Integer::from(2*k*(2*k+1));

I received error

Why rug is not in playground? The Integer type stores something on the heap.

Please provide the full error message. rustc works very hard to make pretty ascii diagrams showing where a value was moved.

rug is not on the playground because the playground only has the top N crates. I don't remember if it's the top 1000 or 5000 or some other number.

Here is the full error description:

Try replacing

let mut num = Rational::from((k, denom));

with

let mut num = Rational::from(&(k, denom));
//                           ^ Note the ampersand

Rational has a From implementation that takes a &(Num, Den): impl

2 Likes

Thank you a lot. Now, I have the following code

use rug::{ Integer, Rational };

fn main() {
	const N: usize = 20;
	let mut c: [Rational; N];
	let mut nom : Integer;
	let mut denom : Integer;
	
	denom = Integer::from(1);

	for k in 1..N+1 {
		denom *= Integer::from(2*k*(2*k+1));
		let mut num = Rational::from((k, 1));
		num /= &denom;
		let mut den : Integer;
		
		den = Integer::from(1);
		
		for i in 1..k {
			den *= Integer::from(2 * i * (2 * i + 1));
			let mut summand : Rational;
			
			summand = Rational::from((1, 1));
			summand *= &(c[k - i - 1]);
			summand /= &den;
			num -=  summand;
		}
		
		c[k - 1] = num;
		println!("{}", num);
	}
}

I have the following error:

The error is fairly straightforward. c is not initialized. You only declare it:

If you want to fill it with zeros, add = [Rational::whateverfunctioncallyieldszero(); N].

This only works with Copy types. In this case, using

let mut c: [Rational; N] = Default::default();

works as long a N ≤ 32. For alternatives for larger N, refer to this website.

1 Like

Yes, but I can not find such method of the Rational type. Compiler reports that Rational type has no Copy method. How to fix it?

I played with your code a bit to make it better ("better" in a subjective sense; I hope I haven’t changed any behavior; in particular I tried to avoid some allocations by reusing variables; I’ll admit, the u16 vs usize vs u64 is a bit annoying - the idea is that u64 is large enough for multiplying u16s and u16 can be converted into usize without any possibility for failure). This is the result:

use rug::{Integer, Rational, Assign};

fn main() {
    const N: u16 = 40;
    let mut c: Vec<Rational> = Vec::with_capacity(N.into());

    let mut summand = Rational::new();
    let mut den = Integer::new();
    let mut denom = Integer::from(1);

    let helper_fun = |n: u64| -> u64 {2 * n * (2 * n + 1)};
    for k in 1..=N {
        denom *= helper_fun(k.into());
        let mut num = Rational::from((k, &denom));
        den.assign(1);

        for i in 1..k {
            den *= helper_fun(i.into());
            summand.assign(&c[usize::from(k - i - 1)] / &den);
            num -= &summand;
        }

        println!("{}", num);
        c.push(num);
    }
}
1 Like

Thank you a lot. My initial question "is it possible without sequence on heap" (by slices instead of vectors) remains.

I played with code and obtained this:

use rug::{Integer, Rational, Assign};

fn main() {
    const N: u16 = 8;
    let mut c: Vec<Rational> = Vec::with_capacity(N.into());

    let mut summand = Rational::new();
    let mut den = Integer::new();
    let mut denom = Integer::from(1);

    let helper_fun = |n: u64| -> u64 {2 * n * (2 * n + 1)};
    for k in 1..=N {
        denom *= helper_fun(k.into());
        let mut num = Rational::from((k, &denom));
        den.assign(1);

        for i in 1..k {
            den *= helper_fun(i.into());
            summand.assign(&c[usize::from(k - i - 1)] / &den);
            num -= &summand;
        }

        c.push(Rational::from(&num));
		let multiplier = Rational::from(&num).abs();
		let val = f64::powf(std::f64::consts::PI, (2*k) as f64)*multiplier.to_f64();
        println!("The sum of 1/n^{} when n goes from 1 to infinity is \
		pi^{} times {} = 1+1/{} = {}.", 2*k, 2*k, multiplier, 1.0/(val-1.0), val);
    }
}

Can anybody answer my initial question? Is is possible using slices, without vectors?

The link @steffahn provided shows how to do it yourself (search for "Rust 1.36") or suggests a number of crates that can supply a macro to do it for you (search for "Prior Art").

I don’t see much value in avoiding the Vec since the arbitrary precision integers do way more allocation anyways. But it is possible. If you really want to avoid the vec, another drop-in replacement for the Vec, also while keeping the array on the stack would be to use the arrayvec crate.

I think the way to initialize it should look like

let mut c = ArrayVec::<[Rational; N as _]>::new();

once you add the crate dependency and import the ArrayVec type.
The rest of the code can probably stay the same as with Vec.

1 Like

Thank you a lot. The point of avoiding vectors was in understanding language concepts.