Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Appendix B: Coming From Another Language

From Lux

Lux was built to be outgrown, and Rust is one of the three languages it was built to be outgrown into. Most of what you learned transfers without translation, because Lux took its data model from the same family Rust belongs to: values that hold still unless you say otherwise, no null, a match that has to cover every case, and a failure that is a value rather than a hidden mechanism.

LuxRust
let x = 5let x = 5;
var x = 5let mut x = 5;
int, float, string, booli64 (or i32), f64, String, bool
[int]Vec<i64>, or &[i64] when borrowed
func f(n: int) -> int { }fn f(n: i64) -> i64 { }
return n * 2n * 2 as the last line, or return n * 2;
print("x is", x)println!("x is {x}")
length(xs)xs.len()
queue += 4queue.push(4);
for x in xsfor x in &xs
for i in 0..5for i in 0..5
struct Point { x: int }struct Point { x: i64 }
Point(x: 3, y: 4)Point { x: 3, y: 4 }
enum Shape { circle(radius: float) }enum Shape { Circle(f64) }
Shape.circle(radius: 2.0)Shape::Circle(2.0)
match s { circle(let r) => ... }match s { Shape::Circle(r) => ..., }
some(x), noneSome(x), None
ok(x), err(reason)Ok(x), Err(reason)
Option<int>, Result<int, string>Option<i64>, Result<i64, String>
int(3.9), string(42)3.9 as i64, 42.to_string()
parseInt(s)s.parse::<i64>(), which gives a Result
readFile(path)std::fs::read_to_string(path)
eprint(msg)eprintln!("{msg}")
func main() { }fn main() { }, and it is required

The mechanical differences are semicolons at the ends of statements, commas between match arms, :: where Lux writes a dot for a namespace, CamelCase variant names, and the type names being explicit about width. Fix those and most Lux programs are nearly Rust.

Four differences are real.

Ownership. Lux copies values; assigning an array gives you an independent array. Rust moves them, and the original becomes unusable. This is Chapter 5, and it is the largest single difference between the two languages. When it frustrates you, .clone() is the Lux behavior, spelled out loud.

Mutation is one word, and it is a different word. var becomes let mut, and mut also appears on parameters, on references, and on methods. Lux asks whether a name can be reassigned; Rust asks that, and also who is allowed to modify a value through a borrow.

parseInt gives an Option, parse gives a Result. Rust tells you why the parse failed. Use .ok() when you want the Lux shape.

Strings are bytes underneath. length(s) in Lux counts characters, and s.len() in Rust counts bytes. Use s.chars().count() for the Lux answer. Chapter 7 explains why Rust makes you choose.

You also have a tool. lux convert rust program.lux prints the Rust translation of any Lux program you have written, and comparing the two is a fast way to learn the mapping in the terms of code you already understand. The output is deliberately literal, and it clones more than a Rust programmer would, for the reason Chapter 5 gives.

From Python

PythonRust
x = 5let mut x = 5;, or let x = 5; when it never changes
def f(n): return n * 2fn f(n: i64) -> i64 { n * 2 }
print(f"x is {x}")println!("x is {x}");
[1, 2, 3]vec![1, 2, 3]
{"a": 1}HashMap::from([("a", 1)])
{1, 2}HashSet::from([1, 2])
(1, "two")(1, "two")
for x in xs:for x in &xs {
[f(x) for x in xs if p(x)]xs.iter().filter(|x| p(x)).map(|x| f(x)).collect()
len(xs)xs.len()
xs.append(v)xs.push(v)
d.get(k)d.get(&k), an Option
NoneNone, but the type must say Option<T>
try / exceptResult and ?
raisereturn Err(...), or panic! for a bug
with open(p) as f:scope ends, the file closes itself
class C:struct C plus impl C
isinstance(x, C)usually an enum and a match
int("42")"42".parse::<i64>(), a Result
# type: ignoreno equivalent, and that is the point

The adjustments that take longest: every type is fixed at compile time, integers do not grow without bound, a dictionary lookup gives you an Option rather than a KeyError, and there is no truthiness.

From JavaScript

JavaScriptRust
const x = 5let x = 5;
let x = 5let mut x = 5;
function f(n) { return n * 2 }fn f(n: i64) -> i64 { n * 2 }
n => n * 2|n| n * 2
[1, 2, 3]vec![1, 2, 3]
{a: 1} as a recorda struct
{} as a lookup tablea HashMap
xs.map(f).filter(p)xs.iter().map(f).filter(p).collect()
xs.lengthxs.len()
null and undefinedOption<T>, and there is only one of it
try / catchResult and ?
throwreturn Err(...), or panic! for a bug
=====, which is the only kind there is
JSON.parseserde_json, a dependency
async / awaitasync / await, with a runtime you choose
npm installcargo add

The adjustments: numbers are not all floats, == compares values of the same type and refuses anything else, there is no implicit conversion anywhere, and the absence of a value has one spelling rather than two.