A helper lesson for the course "Build your own arbitrage bot". Lesson 13 moves the hot loop of the scanner into Rust. You do not need to become a Rust programmer, only to read a short program and run it.
The head-on way: stay in Python
Python is fine for the first scanner. The trouble starts when one function must run millions of times a second: the interpreter does extra work for every step. A compiled language such as Rust turns the program into machine code once, before it runs, and the loop then costs almost nothing per step. The price is that you write a little more and the compiler is strict.
A small program
The same four prices as in the previous primer. Save this as r.rs:
fn mean(v: &Vec<f64>) -> f64 {
let mut sum = 0.0;
for x in v {
sum += x;
}
sum / v.len() as f64
}
fn main() {
let prices: Vec<f64> = vec![100.0, 101.5, 99.8, 102.0];
let mut best = 0.0;
for i in 0..prices.len() - 1 {
let ratio = prices[i + 1] / prices[i];
println!("step {}: ratio {:.4}", i, ratio);
if ratio > best {
best = ratio;
}
}
println!("best {:.4}, mean price {:.3}", best, mean(&prices));
}
Build and run it: rustc -O r.rs -o r && ./r.
step 0: ratio 1.0150
step 1: ratio 0.9833
step 2: ratio 1.0220
best 1.0220, mean price 100.825
How to read it
fn name(...) -> type { ... }defines a function. The last expression without a semicolon is what it returns:sum / v.len() as f64gives the mean.letmakes a variable. It cannot change unless you writelet mut. That is whysumandbesthavemut.Vec<f64>is a list of decimal numbers (a vector).vec![...]creates one.v.len()is its length.for i in 0..nloops i from 0 up to n minus 1.for x in vloops over the items.&pricesmeans "lend the vector to the function, do not give it away". You can skip the details for now: if the compiler complains about ownership, show the message to your agent.println!prints.{}is a place for a value,{:.4}is a decimal number with 4 digits after the point.- Types are written down (
f64), and the compiler refuses a program where they do not fit. It feels strict, but it catches mistakes before the program runs.
Why the flag -O matters
Without -O Rust builds a slow version for debugging. With it, the compiler optimizes. A loop of a billion steps on one machine:
without -O: 1.18 s
with -O: 0.39 s
(Your numbers will differ.) Always measure a release build, the one made with -O, or with cargo build --release if you use cargo.
Your exercise
- Install Rust (rustup.rs), save the program and run it. Match the output.
- Add a fifth price and make the program print the worst ratio too.
- Write a function
max_of(v: &Vec<f64>) -> f64and print its result. - Build with and without
-Oa loop of 300 million steps and compare the time. - Ask your agent to explain the first compiler error you meet, in plain words.
What comes next
You can read the Rust code of lesson 13 now.