Exploring Rust: Influences from Other Programming Languages
Written on
Chapter 1: The Essence of Rust
In this journey through Rust, an innovative systems programming language developed by Mozilla, we will examine its unique blend of performance, security, and expressiveness. Rust is a product of diverse programming influences, and in this exploration, we will uncover the significant contributions from various languages that have shaped its design and functionality.
Section 1.1: Ownership System — A Nod to C++
Recently, I engaged in a lively discussion on Twitter regarding C++ and its features. One commenter dismissed RAII (Resource Acquisition Is Initialization) as a mere "nonsense feature." This perspective surprised me since RAII is a well-conceived and elegantly designed aspect of C++. It’s gratifying to see how RAII has been embraced in Rust.
Consider the following example:
struct Foo {
data: i32,
}
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo with data: {}", self.data);}
}
fn main() {
let _foo = Foo { data: 5 };
} // _foo is dropped here, invoking the drop method.
In this code, we define a struct Foo and implement a Drop trait to manage its resource. When _foo goes out of scope, the drop function is automatically called, illustrating the essence of RAII: automatic resource cleanup upon scope exit.
RAII is foundational in Rust's standard library. For instance, when using File::open, the file is automatically closed when the File object goes out of scope, preventing resource leaks and simplifying resource management. Rust’s ownership system, influenced by C++, ensures efficient memory management without relying on garbage collection.
#include <iostream>
#include <string>
int main() {
std::string s = "Hello";
const std::string& r1 = s;
const std::string& r2 = s;
std::cout << r1 << " and " << r2 << std::endl;
return 0;
}
In contrast, Rust handles similar operations seamlessly:
fn main() {
let mut s = String::from("Hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
} // r1 and r2 exit scope without memory issues.
Section 1.2: Concurrency — Inspired by Erlang
Rust’s approach to concurrency, which prioritizes safety while maintaining high performance, is heavily influenced by Erlang. Its ownership and borrowing model guarantees thread safety without the necessity of a garbage collector. Here’s how concurrency is handled in Rust:
Erlang example:
-module(main).
-export([main/0]).
main() ->
spawn(fun() -> thread_function() end),
main_thread_function().
thread_function() ->
lists:foreach(fun(I) -> io:format("Thread: ~w~n", [I]) end, lists:seq(1, 5)).
main_thread_function() ->
lists:foreach(fun(I) -> io:format("Main thread: ~w~n", [I]) end, lists:seq(1, 3)).
Rust equivalent:
use std::thread;
fn main() {
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("Thread: {}", i);}
});
for i in 1..=3 {
println!("Main thread: {}", i);}
handle.join().unwrap();
}
Section 1.3: Pattern Matching — Borrowed from Haskell
Pattern matching in Rust, a powerful feature, is greatly inspired by Haskell. It enables concise and expressive handling of various data types.
Haskell example:
data Coin = Penny | Nickel | Dime | Quarter
valueInCents :: Coin -> Int
valueInCents coin = case coin of
Penny -> 1
Nickel -> 5
Dime -> 10
Quarter -> 25
main :: IO ()
main = do
let coin = Dime
putStrLn $ "Value: " ++ show (valueInCents coin) ++ " cents"
Rust equivalent:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn main() {
let coin = Coin::Dime;
println!("Value: {} cents", value_in_cents(coin));
}
Section 1.4: Traits — Inspired by Haskell Type Classes and Scala Traits
Rust's trait system, akin to Haskell's type classes and Scala's traits, facilitates code reuse and defines shared behavior.
Scala example:
trait Greet {
def greet(): String
}
class Person(val name: String) extends Greet {
def greet(): String = s"Hello, $name!"
}
object Main extends App {
val person = new Person("Alice")
println(person.greet())
}
Rust equivalent:
trait Greet {
fn greet(&self) -> String;
}
struct Person {
name: String,
}
impl Greet for Person {
fn greet(&self) -> String {
format!("Hello, {}!", self.name)}
}
fn main() {
let person = Person { name: String::from("Alice") };
println!("{}", person.greet());
}
Rust's architecture embraces the finest features from various programming languages, resulting in a powerful, safe, and expressive language. Whether it’s the ownership system echoing C++, the concurrency model reflecting Erlang's principles, or the pattern matching reminiscent of Haskell, Rust stands out as a synthesis of its predecessors' strengths.
As we continue to explore Rust, we may uncover even more influences from other languages.
Chapter 2: Additional Resources on Rust
In this insightful video, Carol Nichols discusses why Rust is poised to be a programming language for the next 40 years, highlighting its unique features and community.
This video compares Rust with seven other languages that you might not have explored yet, showcasing Rust's distinct advantages and design philosophies.
Other Readings on Rust:
- Rust vs Go
- Rust vs Go (heated debate version)
- A Rapid Guide to All Rust Features
- String Secrets in Rust and Go
Reference: