Rustあれこれ

Rustとは

・2015年に正式リリース
・実行速度が速い
・静的型付け言語
・メモリ安全性の保証
機械語に直接コンパイルされる
GCガベージコレクションをもたない)

JavaPythonなどは独自の仮想マシンを持つ
仮想マシンを入れるメリット
 さまざまな環境下でプログラミング言語を実行しやすくなる点
インタプリタコンパイラを介すと仮想マシン用の言語を生成する。
仮想マシンはソフトウェア上に実装されているため、速度面で不利になる。
C言語C++、Rustなどはコンパイル後の最終結果は機械語になる。
仮想マシンを介さずに実行できるため仮想マシンに起因する速度低下が起きない。
また、さまざまな環境下で実行できるよう
GNU Compiler Collection(GCC)やLLVMといったコンパイラ
環境に応じた機械語を生成するようになっている。

インストール

doc.rust-lang.org

・パッケージ作成

cargo new <パッケージ名>

・ビルド

cargo build

※バイナリファイルはtargetファイルへ格納される

・ビルド+実行

cargo run

コンパイルによるコードチェック

cargo check

モジュール作成

// publicで公開
pub fn run() {
  println!("Here is vars module!");
}
mod vars;

fn main() {
  vars::run();
}
cargo run
Here is vars module!
サブモジュール
pub fn func_a() {
  println!("func a called!");
}
pub fn func_b() {
  println!("func b called!");
}
pub mod sub_a.rs;
pub mod sub_b.rs;

pub fn run() {
  println!("Here is vars module!");
  // 関数の呼び出し
  sub_a::func_a();
  sub_b::func_b():
}
mod vars;

fn main() {
  vars::run();
  vars::sub_a::func_a();
  vars::sub_b::func_b();
}
cargo run
Here is vars module!
func a called!
func b called!
func a called!
func b called!

メモリについて

Heap(ヒープ)
 可変長データ(String, Vector)、容量が大きく可変長なデータを扱うことができるが
 メモリへのアクセスはスタックに比べると遅くなる
Stack(スタック)
 サイズが決まった変数や配列、容量は限られているが高速なメモリアクセスができる
Static(静的領域)
 const、文字列リテラルの実体
Text(コード)
 バイナリコードが保存される領域

Last In Fast Out (Stack)高速
スタックは変数や配列がスタック上に積まれていき、
取り出すとき(pop)は一番最後に積まれたものが最初に取り出される
popで取り出してメモリを解放するときに
どれだけのデータ量を取り出していいかというのが事前にわかっていないと
データの整合性が取れなくなるためスタックに格納されるデータは
あらかじめデータサイズが決まっている必要がある

Mutable・Immutable
pub mod sub_a;
pub mod sub_b;

const MAX_POINTS: u32 = 100_000;

pub fn run() {
  println!("Here is vars module!");
  // sub_a::func_a();
  // sub_b::func_b();
  let mut x = 5;
  println!("The value of x is: {}", x);
  x = 6;
  println!("The value of x is: {}", x);
  let _i1 = 3;
  let _f1 = 0.1;

  println!("{}", usize::BITS);
  println!("Memory address of const is: {:p}", &MAX_POINTS);

  let i2: i64 = 1;
  let i3: i64 = 2;
  println!("Stack address of i2 is: {:p}", &i2);
  println!("Stack address of i3 is: {:p}", &i3);

  let y = 5;
  println!("Stack address of y is: {:p}", &y);
  let y = y + 1;
  println!("Stack address of y is: {:p}", &y);
  let y = y * 2;
  println!("Stack address of y is: {:p}", &y);
  println!("The value of y is: {}", y);
  {
    let y = 0;
    println!("The value of y is: {}", y);
  }
  println!("The value of y is: {}", y);

  let t1 = (500, 6.4, "dummy");
  let (_x, _y, _z) = t1;
  println!("The value of t1 is: {} {} {}", t1.0, t1.1, t1.2);

  let mut t2 = ((0, 1), (2, 3));
  let ((ref mut x1_ptr, ref mut y1_ptr), _) = t2;
  *x1_ptr = 5;
  *y1_ptr = -5;
  println!("{:?}", t2);

  let a1 = [1, 2, 3, 4, 5];
  let a2 = [0; 10];
  println!("{:?} {:?} {} {}", a1, a2, a1[2], a1[3]);

  let s1 = "helloこんにちは挨拶"; //26bytes
  let s2 = "hello";
  println!("Stack address of s1 is: {:p}", &s1);
  println!("Stack address of s2 is: {:p}", &s2);
  println!("Stack memory address of s1 is: {:?}", &s1.as_ptr());
  println!("Stack memory address of s2 is: {:?}", &s2.as_ptr());
  println!("Len of s1 is: {}", &s1.len());
  println!("Len of s2 is: {}", &s2.len());

  let mut s1 = String::from("hello");
  let mut s2 = String::from("helloworld");
  println!("Stack address of s1 is: {:p}", &s1);
  println!("Stack address of s2 is: {:p}", &s2);
  println!("Stack memory address of s1 is: {:?}", s1.as_ptr());
  println!("Stack memory address of s2 is: {:?}", s2.as_ptr());
  println!("Len of s1 is: {}", s1.len());
  println!("Len of s2 is: {}", s2.len());
  println!("Capacity of s1 is: {}", s1.capacity());
  println!("Capacity of s2 is: {}", s2.capacity());
  s1.push_str("_new1");
  s2.push_str("_new2");
  println!("{} {}", s1, s2);
}
Stack overflow・Vector
enum List {
  Node(i32, Box<List>),
  Nil,
}

pub fn run() {
  // let a1: [u8; 9000000] = [1; 9000000];

  let mut v1 = vec![1, 2, 3, 4];
  let v2 = vec![5, 6, 7, 8];
  let mut v3 = vec![9, 10];
  println!("Stack address of v1 is: {:p}", &v1);
  println!("Stack address of v2 is: {:p}", &v2);
  println!("Heap memory address of v1: {:?}", v1.as_ptr());
  println!("Len of v1 is: {}", v1.len());
  println!("Capacity of v1 is: {}", v1.capacity());
  v1.insert(1, 10);
  println!("{:?}", v1);
  v1.remove(0);
  println!("{:?}", v1);
  v1.append(&mut v3);
  println!("{:?}", v1);
  println!("{:?}", v3);

  let t1: (i64, String) = (10, String::from("hello"));
  println!("Stack address of tuple data is: {:p}", &t1);
  println!("Heap memory address of t1: {:?}", t1.1.as_ptr());
  println!("Len of v1 is: {}", t1.1.len());
  println!("Capacity of v1 is: {}", t1.1.capacity());
  let mut b1 = Box::new(t1);
  (*b1).1 += "world";
  println!("{} {}", b1.0, b1.1);
  println!("Stack address of box pointer is: {:p}", &b1);
  println!("Heap address of tuple data is: {:p}", b1);
}
所有権・参照・借用
pub fn run() {
  let s1 = String::from("hello");
  let s2 = s1;
  println!("{}", s2);

  let i1 = 1;
  let i2 = i1;
  println!("{} {}", i1, i2);
  println!("Stack address of i1 is: {:p}", &i1);
  println!("Stack address of i2 is: {:p}", &i2);

  let sl1 = "literal";
  let sl2 = sl1;
  println!("{} {}", sl1, sl2);
  println!("Stack address of sl1 is: {:p}", &sl1);
  println!("Stack address of sl2 is: {:p}", &sl2);

  let s3 = String::from("hello");
  let s4 = s3.clone();
  println!("Stack address of s3 is: {:p}", &s3);
  println!("Stack address of s4 is: {:p}", &s4);
  println!("Heap memory address of hello: {:p}", &s3.as_ptr());
  println!("Heap memory address of hello: {:p}", &s4.as_ptr());
  println!("{} {}", s3, s4);

  let s5 = String::from("hello");
  println!("Stack address of s5: {:p}", &s5);
  println!("Heap address of hello: {:?}", s5.as_ptr());
  println!("Len is: {}", s5.len());
  println!("Cap is: {}", s5.capacity());
  take_ownership(s5);
  // println!("{}", s5);
  let s6 = String::from("hello");
  println!("Stack address of s6: {:p}", &s6);
  println!("Heap address of hello: {:?}", s6.as_ptr());
  println!("Len is: {}", s6.len());
  let s7 = take_giveback_ownership(s6);
  println!("Stack address of s7: {:p}", &s7);
  println!("Heap address of hello: {:?}", s7.as_ptr());
  println!("Len is: {}", s7.len());

  let s8 = String::from("hello");
  let len = calculate_length(&s8);
  println!("The length of '{}' is {}", s8, len);

  let mut s9 = String::from("hello");
  change(&mut s9);
  println!("{}", s9);

  let s10 = String::from("hello");
  let r1 = &s10;
  let r2 = &s10;
  println!("{} {} {}", s10, r1, r2);

  // let mut s10 = String::from("hello");
  // let r1 = &s10;
  // let r2 = &mut s10;
  // println!("{} {}", r1, r2);

  let mut s11 = String::from("hello");
  let r1 = &mut s11;
  println!("{}", r1);
  println!("{}", s11);
  let mut s12 = String::from("hello");
  let r1 = &s12;
  let r2 = &s12;
  println!("{} and {}", r1, r2);
  let r3 = &mut s12;
  *r3 = String::from("hello_updated");
  println!("{}", s12);
}
fn take_ownership(s: String) {
  println!("Stack address of s: {:p}", &s);
  println!("Heap address of hello: {:?}", s.as_ptr());
  println!("Len is: {}", s.len());
  println!("Cap is: {}", s.capacity());
  println!("{}", s);
}
fn take_giveback_ownership(s: String) -> String {
  s
}
fn calculate_length(s: &String) -> usize {
  s.len()
}
fn change(s: &mut String) {
  s.push_str("_world");
}
Life time・Dangling pointer
pub fn run() {
  let st1 = String::from("x");
  let st2 = String::from("y");
  let res1 = get_longest(&st1, &st2);
  println!("{}", res1);

  let st3 = String::from("x");
  let res2;
  {
    let st4 = String::from("y");
    res2 = get_longest(&st3, &st4);
    println!("{}", res2);
  }
}
fn get_longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  if x.len() > y.len() {
    x
  } else {
    y
  }
}
// fn dummy1<'a>() -> &'a str {
//   let s = String::from("demo");
//   &s
// }
// fn dummy2<'a>() -> &'a i32 {
//   let x = 10;
//   &x
// }
fn dummy3() -> String {
  let s = String::from("demo");
  s
}
Generics
struct Point<T> {
  x: T,
  y: T,
}
struct PointAnother<T, U> {
  x: T,
  y: U,
}
impl<T, U> PointAnother<T, U> {
  fn mixup<V, W>(self, other: PointAnother<V, W>) -> PointAnother<T, W> {
    PointAnother {
      x: self.x,
      y: other.y,
    }
  }
}
pub fn run() {
  let number_list = vec![34, 50, 25, 100, 65];
  // let mut largest = number_list[0];
  // for number in number_list {
  //   if number > largest {
  //     largest = number;
  //   }
  // }
  // println!("The largest is {}", largest);
  // println!("{}", largest_i32(number_list));
  let char_list = vec!['a', 'b', 'c', 'd'];
  println!("{}", largest(char_list));
  println!("{}", largest(number_list));
  let p1 = Point { x: 1, y: 2 };
  let p2 = Point { x: 1.0, y: 2.0 };
  let p3 = PointAnother { x: 5, y: 10.4 };
  let p4 = PointAnother { x: "Rust", y: 'a' };
  let p5 = p3.mixup(p4);
  println!("{} {}", p5.x, p5.y);
}
fn largest_i32(list: Vec<i32>) -> i32 {
  let mut largest = list[0];
  for item in list {
    if item > largest {
      largest = item;
    }
  }
  largest
}
fn largest<T: PartialOrd + Copy>(list: Vec<T>) -> T {
  let mut largest = list[0];
  for item in list {
    if item > largest {
      largest = item;
    }
  }
  largest
}
Struct・Enum・Pattern matching
#[derive(Debug)]
struct User {
  username: String,
  email: String,
  sign_in_count: u64,
  active: bool,
}
#[derive(Debug)]
struct Rectangle {
  width: u32,
  height: u32,
}
impl Rectangle {
  fn create(width: u32, height: u32) -> Self {
    Self { width, height }
  }
  fn area(&self) {
    println!("{}", self.width * self.height);
  }
}

pub fn run() {
  let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someoneusername123"),
    active: true,
    sign_in_count: 1,
  };
  let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someoneusername123"),
    active: true,
    sign_in_count: 1,
  };
  user1.email = String::from("anotheremail@example.com");
  println!("{:#?}", user1);
  let user2 = build_user(String::from("user2@xxx.com"), String::from("user2"));
  println!("{:#?}", user2);

  let rect = Rectangle::create(20, 20);
  println!("{:#?}", rect);
  rect.area();
  println!("{:#?}", rect);
}
fn build_user(email: String, username: String) -> User {
  User {
    email,
    username,
    active: true,
    sign_in_count: 1,
  }
}
Traits
trait Fruits {
  fn price(&self) -> u32;
}
struct Apple;

impl Fruits for Apple {
  fn price(&self) -> u32 {
    10
  }
}

struct Banana;
impl Fruits for Banana {
  fn price(&self) -> u32 {
    5
  }
}
trait Summary {
  fn summarize(&self) -> String {
    String::from("(Read more...)")
  }
}
trait Message {
  fn message(&self) -> String {
    String::from("Message")
  }
}
struct NewsArticle {
  headline: String,
  location: String,
  author: String,
  content: String,
}
impl Summary for NewsArticle {
  // fn summarize(&self) -> String {
  //   format!("{}, by {} ({})", self.headline, self.author, self.location)
  // }
}
impl Message for NewsArticle {}
struct Tweet {
  username: String,
  content: String,
  reply: bool,
  retweet: bool,
}
impl Summary for Tweet {
  fn summarize(&self) -> String {
    format!("{}: {}", self.username, self.content)
  }
}

pub fn run() {
  let apple = Apple {};
  let banana = Banana {};
  get_price(apple);
  get_price(banana);
  let tweet = Tweet {
    username: String::from("horse_ebooks"),
    content: String::from("of course, as you probably already know, people"),
    reply: false,
    retweet: false,
  };
  println!("1 new tweet: {}", tweet.summarize());
  let article = NewsArticle {
    headline: String::from("Penguins win the Stanley Cup Championship!"),
    location: String::from("Pittsburgh, PA, USA"),
    author: String::from("Iceburgh"),
    content: String::from(
      "The Pittsburgh Penguins once again are the best hockey team in the NHL.",
    ),
  };
  println!("{}", article.summarize());
  notify(&article);
  notify_another(&article);
}
fn get_price<T: Fruits>(fruits: T) {
  println!("price is: {}", fruits.price());
}
fn notify(item: &impl Summary) {
  println!("Breaking news! {}", item.summarize());
}
fn notify_another(item: &(impl Summary + Message)) {
  println!("Breaking news! {}", item.summarize());
  println!("Message! {}", item.message());
}
error handling
pub fn run() {
  let res1 = division_option(5.0, 0.0);
  match res1 {
    Some(x) => println!("Result: {:.3}", x),
    None => println!("Not allowed !!"),
  }
  let res2 = division_result(5.0, 1.0);
  match res2 {
    Ok(x) => println!("Result: {:.3}", x),
    Err(e) => println!("{}", e),
  }
  let a = [0, 1];
  let res3 = sum(&a);
  match res3 {
    Some(x) => println!("Total is: {}", x),
    None => println!("Out of index !!"),
  }
}
fn division_option(x: f64, y: f64) -> Option<f64> {
  if y == 0.0 {
    None
  } else {
    Some(x / y)
  }
}
fn division_result(x: f64, y: f64) -> Result<f64, String> {
  if y == 0.0 {
    Err(String::from("Not allowed !!"))
  } else {
    Ok(x / y)
  }
}
fn sum(a: &[i32]) -> Option<i32> {
  let a0 = a.get(0)?;
  let a1 = a.get(1)?;
  let a2 = a.get(2)?;
  Some(a0 + a1 + a2)
}
Unit test
struct Rectangle {
  width: u32,
  height: u32,
}
impl Rectangle {
  fn compare_area(&self, other: &Rectangle) -> bool {
    self.width * self.height > other.width * other.height
  }
}

fn double_value(a: i32) -> i32 {
  a * 2
}
fn greeting(name: &str) -> String {
  format!("Hello {} san", name)
}

#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn test_a_is_larger() {
    let a = Rectangle {
      width: 5,
      height: 5,
    };
    let b = Rectangle {
      width: 3,
      height: 3,
    };
    assert!(a.compare_area(&b));
  }
  #[test]
  fn test_a_is_smaller() {
    let a = Rectangle {
      width: 3,
      height: 3,
    };
    let b = Rectangle {
      width: 5,
      height: 5,
    };
    assert!(!(a.compare_area(&b)));
  }
  #[test]
  fn test_double() {
    assert_eq!(6, double_value(3));
  }
  #[test]
  fn test_contains_name() {
    let res = greeting("rust");
    assert!(res.contains("rust"));
  }
}