Rust 実行速度

use std::os::raw::c_int;

#[link(name="mylib", kind="static")]
extern "C" {
	fn twice(n: c_int) -> c_int;
}

fn main() {
	let n = 12;
	unsafe {
		println!("{}の2倍は{}", n, twice(n));
	}
}
use std::time::Instant;

fun main() {
	i16func();
	i64func();
}

fn i16func(){
	let mut v:16 = 0;
	let start = Instant::now();
	for _ in 0..1000 {
		for _ in 0..10000 {
			v = v + 1;
		}
		v = v - 10000;
	}
	let elepse = start.elapsed();
	println!("i16={:?}", v);
}

fn i64func() {
	let mut v:i64 = 0;
	let start = Instant::now();
	for _ in 0..1000 {
		for _ in 0..10000 {
			v = v + 1;
		}
		v = v- 10000;
	}
	let elepse = start.elapsed();
	println!("i64={:?}", v);
	println!("{}ミリ秒", elepse.as_millis());
}
use std::time::Instant;

fn main() {
	f32func();
	f64func();
}

fn f32func() {
	const NDATA:usize = 10000;
	let mut a:[f32; NDATA] = [0.0; NDATA];
	for i in 0..NDATA {
		a[i] = (NDATA - i) as f32;
	}
	println!("並び替えスタート");
	let start = Instant::now();
	for i in 0..NDATA{
		for j in 0..NDATA {
			if a[i] < a[j] {
				let tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}
	let elepse = start.elapsed();
	println!("並べ替え終了");
	println!("{}ミリ秒", elepse.as_millis());
}

fn f64func() {
	const NDATA:usize = 10000;
	let mut a:[f64; NDATA] = [0.0; NDATA];
	for i in 0..NDATA {
		a[i] = (NDATA - i) as f64;
	}
	let start = Instant::now();
	for i in 0..NDATA {
		for j in 0..NDATA {
			if a[i] < a[j] {
				let tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}
	let elepse = start.elapsed();
	println!("並び替え終了");
	println!("{}ミリ秒", elepse.as_millis());
}

Rust パニックとアサート

プログラムの実行が継続できないような重大な問題をパニックという
パニックは他の言語の例外処理に似ている
Cargo.tomlファイルのpanic=’abort’で異常終了する

let f = File::open("sample.txt");
let f = match f {
	Ok(file) => file,
	Err(error) => {
		panic!("ファイル開けません: {:?}", error)
	}
}

アサートはプログラムの途中で値が妥当であるか調べる
assert_eq!(left, right);

let f = 3.7_f64;

assert_eq!(f.floor(), 3.0);
println!("f={}", f);

### メモリ管理
メモリはランタイムが管理するので考える必要はない

Box: std::boxed::Boxを使うことでヒープにメモリを確保することができる

struct Point {
	x: i32,
	y: i32,
}

impl Clone for Poin {
	fn clone(&self) -> Self {
		Point {
			x: self.x.clone(),
			y: self.y.clone(),
		}
	}
}

fn main() {
	let p1 = Point {x: 12, y:25};
	let p2: Box<Point> = Box::new(Point {x: 23, y: 45});
	println!("{:?}", type_of(p1.clone()));
	println!("{:?}", type_of(p2.clone()));
	println!("{}, {}", p1.x, p1.y);
	println!("{}, {}", p2.x, p2.y);
}

fn type_of<T>(_: T) -> &'static str {
	std::any::type_name::<T>()
}
use std::mem;

struct Point {
	x: i32,
	y: i32,
}

fn main() {
	let p = Point {x: 12, y: 25};
	let a = [1,2,3,4,5,6,7,8,9];

	println!("pのサイズ={}", mem::size_of_val(&p));
	println!("aのサイズ={}", mem::size_of_val(&a));
}

Rust ネットワーク

TCPというプロトコルを使って情報を受け渡すサーバとクライアント

サーバ側

use std::net::{TcpListener,TcpStream};
use std::thread;
use std::io::{Read,Write,Error};

fn handle_data(mut stream: TcpStream) -> Result<(), Error> {
	println!("Connection from: {}", stream.peer_addr()?);
	let mut buf = [0; 512];
	loop {
		let byte_read = stream.read(&mut buf)?;
		for i in 0..byte_read {
			if buf[i] > 0x60 {
				buf[i] = buf[i] - 0x20;
			}
		}
		if byte_read == 0 {return Ok(());}
		stream.write(&buf[..byte_read])?;
	}
}

fn main() {
	let coninfo = "localhost:8000";
	let listener = TcpListener::bind(coninfo).expect("バインドできません。");
	for steam in listener.incoming(){
		match stream {
			Err(e) => {eprintln!("failed: {}", e)}
			Ok(stream) => {
				thread::spawn(move || {
					handle_data(stream).
						unwrap_or_else(|error| eprintln!("{:?}", error));
				});
			} 
		}
	}
}

クライアント側

use std::net::{TcpStream};
use std::str;
use std::io::{self,BufRead,BufReader,Write};

fn main() {
	let coinfo = "localhost:8000";

	let mut stream = TcpStream::connect(coinfo).expect("サーバに接続できません。");

	loop {
		let mut input = String::new();
		io::stdin().read_line(&mut input).expect("標準入力からの入力エラー");
		stream.write(input.as_bytes()).expect("サーバに送れません。");

		let mut reader = BufReader::new(&stream);
		reader.read_until(b'¥n', &mut buffer).expect("サーバから受け取れません");

		print!("{}", str::from_utf8(&buffer).expect("テキストを表示できません"));
	}
}

じゃんけんサーバ

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Read,Write,Error};
use rand::{thread_rng, Rng};

fn handle_data(mut stream: TcpStream) -> Result<(), Error> {
	println!("Connection from: {}", stream.peer_addr()?);
	let mut buf = [0; 512];
	loop {
		let mut userte = false;
		let mut cpmsg = String::new();
		let mut msg = String::from("");
		let byte_read = stream.read(&mut buf)?;
		if byte_read == 0 {return Ok(());}

		let mut rng = thread_rng();
		let x: u16 = rng.gen_range(0, 3);

		match x {
			0 => cpmsg.insert_str(0, "私はグー"),
			1 => cpmsg.insert_str(0, "私はチョキ"),
			2 => cpmsg.insert_str(0, "私はパー"),
			_ => cpmsg.insert_str(0, ""),
		}
		let client_msg = String::from_utf8(buf.to_vec()).unwrap();
		msg.insert_str(0, &cpmsg);
		if client_msg.starts_with("グー") {
			userte=true;
			match x {
				0 => msg.push_str(":あいこ¥n");
				1 => msg.push_str(":あなたの勝ち¥n");
				2 => msg.push_str(":あなたの負け¥n");
				_ => ();
			}
		} else if client_msg.starts_with("チョキ"){
			userte=true;
			match x {
				0 => msg.push_str(":あなたの負け¥n");
				1 => msg.push_str(":あいこ¥n");
				2 => msg.push_str(":あなたの勝ち¥n");
				_ => ();
			}
		} else if client_msg.starts_with("パー"){
			userte=true;
			match x {
				0 => msg.push_str(":あなたの勝ち¥n");
				1 => msg.push_str(":あなたの負け¥n");
				2 => msg.push_str(":あいこ¥n");
				_ => ();
			}
		} if userte {
			let bytes = msg.as_bytes();
			let len = bytes.len();
			stream.write(&bytes[..len])?;
		} else {
			stream.write(&buf[..byte_read])?;
		}
		stream.flush()?;
	}
}

fn main() {
	let coninfo = "localhost:8000";
	let listener = TcpListener::bind(coninfo).expect("バインドできません。");
	for stream in listenr.incoming() {
		match stream {
			Err (e) => {eprintln!("failed: {}", e)}
			Ok(stream) => {
				thread::spawn(move || {
					handle_data(stream).
						unwrap_or_else(|error| eprintln!("{:?}", error));
				});
			}
		}
	}
}

Rust ファイル入出力

単純なファイル入出力はstd::fsやstd::ioにある関数を使う
std::fs::read(), std::fs::read_dir(), std::fs::read_to_string(), std::fs_write()

use std::fs;

fn main() {
	let result = fs::read_to_string("sample.txt");
	println!("{:?}", result);
}
use std::fs;

fn main() {
	match fs::read_to_string("sample.txt"){
		Ok(result) => println!("{}", result),
		Err(msg) => println!("エラー:{}", msg),
	}
}
use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;

fn main() -> std::io::Result<()> {
	let f = File::open("sample.txt")?;
	let reader = BufReader::new(f);

	for line in reader.lines() {
		println!("{}", line?);
	}
	Ok(())

}
use std::io;
use std::io::prelude::*;
use std::fs::File;

fn main() -> std::io::Result<()> {
	let mut f = File::open("sample.txt")?;
	let mut buffer = [0; 1000];

	let n = f.read(&mut buffer)?;

	println!("{:?}", &buffer[..n]);
	Ok(())
}
use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
	let txt = "こんにちは¥nHappy Rust¥n";
	let mut file = File::create("test.txt")?;
	write!(file, "{}", txt)?;
	file.flush()?;
	Ok(())

}
use std::fs::File;
use std::io::{Write, BufWriter};

fn main() -> std::io::Result<()> {
	let file = File::create("sample2.txt")?;
	let mut writer = BufWrite::new(file);
	let data:&str = "Hello¥nHappy Dogs";
	writer.write_all(data.as_bytes())?;
	writer.flush()?;
	Ok(())
}
use qrcode::QrCode;
use image::Luma;

fn main() {
	let code = QrCode::new(b"A0023213").unwrap();
	let image = code.render::<Luma<u8>>().build();

	image.save("sample.png").unwrap();
}

Rust ファイル操作

Rustのファイルシステムにアクセスするときは、std::fsモジュールにある関数を使うことができる
canonicalize(), copy(), create_dir(), create_dir_all(), hard_link(), metadata(), read_link(), remove_dir(), remove_dir_all(), remove_file(), rename(), set_permissions(), soft_link(), symlink_metadata()

pub fn copy>, Q: AsRef>(from: P, to: Q) -> Result

use std::env;
use std::fs;

fn main() -> std::io::Result<()> {
	let argv: Vec<String> = env::args().collect();
	let argc = argv.len();
	if argc < 3 {
		println!("引数を2個指定ください。");
		std::process::exit(1);
	}

	let src = &argv[1];
	let dest = &argv[2];
	fs::copy(src, dest)?;
	Ok(())
}
use std::fs;

fn main() -> std::io::Result<()> {
	fs::create_dir("./misc/subdir")?;
	Ok(())
}
use std::fs;
use std::path::PathBuf;

fn main() {
	let srcdir = PathBuf::from("./src");
	println!("{:?}", fs::canonicalize(&srcdir));
}

Rust 非同期実行

関数を非同期実行することができる
async/awaitは、時間のかかる処理をするときに、処理が終わるまでのスレッドを別のタスクの処理に使うことができる機能を提供する

use futures::executor::block_on;

fn wait() -> f64 {
	let mut x:f64 = 0.0;
	for _ in 1..10000 {
		for _ in 1..10000 {
			x = x * 1.001;
		}
	}
	x
}

async fn print_dot(){
	println!(".");
}

async fn print_plus(){
	plusfunc().await;
}

async fn print_minus() {
	wait();
	println!("-");
}

async fn plusfunc(){
	wait();
	println!("+");
}

async fn async_main() {
	let f1 = print_plus();
	let f2 = print_minus();
	let f3 = print_dot();
	println!("Hello");
	futures::join!(f1, f2, f3);
}

fn main(){
	block_on(async_main());
}

Rust スレッド間通信、排他制御

並列で実行されているスレッド間で情報を受け渡す
グローバルなstatic変数を使うか、チャンネルを使う

チャンネルではメインスレッドとスレッドとの間で特定の型の値を受け渡す通信機構を提供する
let (tx, rx) = mpsc::channel();
tx.send(count).unwrap();

use std::thread;
use std::time::Duration;
use std::sync::mpsc;

fn main(){
	let (tx, rx) = mpsc::channel();

	let th = thread::spawn(move || {
		let mut count = 0;
		for _i in 1..11 {
			count = count + 1;
			tx.send(count).unwrap();
		}
	});

	let mut cnt = 0;
	for _i in 1..11 {
		cnt = cnt + 1;
		thread::sleep(Duration::from_millis(100));
	}

	th.join().unwrap();
	let val = rx.recv().unwrap();
	println!("カウンタ={}", cnt + val);
	println!("プログラム終了");
}

### 排他制御
複数のスレッドが同じ変数にアクセスすることを避ける方法
std::syncにあるっミューテックスという方法を使って、他のスレッドが変数の値を変更しないようにする
let counter = Arc::new(Mutex::new(0));

use std::thread;
use std::time::Duration;
use std::sync::{Mutex, Arc};

fn main(){
	let counter = Arc::new(Mutex::new(0));

	let counter1 = Arc::clone(&counter);
	let th1 = thread::spawn(mmove || {
		for _i in 1..11 {
			print("+");
			let mut num = counter1.lock().unwrap();
			*num += 1;
			thread::sleep(Duration::from_millis(100));
		}
	});

	let counter2 = Arc::clone(&counter);
	let th2 = thread::spawn(move || {
		for _i in 1..11 {
			let mut num = counter2.lock().unwrap();
			*num += 1;
			print!("-");
			thread::sleep(Duration::from_millis(100));
		}
	});

	th1.join().unwrap();
	th2.join().unwrap();
	println!("\ncounter={:?}", counter);
}

アトミック

use std::thread;
use std::time::Duration;
use std::sync::atomic::{self, AtomicU32, Ordering};

static COUNTER: AtopmicU32 = AtomicU32::new(0);

fn count_up(){
	COUNTER.fetch_add(1, atomic::Ordering::SeqCst);
}
fn get_count() -> u32 {
	return COUNTER.load(Ordering::SeqCst);
}


fn main(){
	let th1 = thread::spawn(mmove || {
		for _i in 1..11 {
			print("+");
			count_up();
			thread::sleep(Duration::from_millis(100));
		}
	});

	let th2 = thread::spawn(move || {
		for _i in 1..11 {
			print!("-");
			count_up();
			thread::sleep(Duration::from_millis(100));
		}
	});

	th1.join().unwrap();
	th2.join().unwrap();
	println!("\ncounter={}", get_count());
}

Rust 並列処理

Rustには1つのプロセスで複数のスレッドを実行するメカニズムも備わっている。そのため、同時に複数のことを行うことができる。

use std::thread;
use std::time::Duration;

fn printch(c: char){
	println!("スレッド{}スタート", c);
	for _i in 1..10 {
		print!("{}", c);
		thread::sleep(Duraion::from_millis(100));
	}
	println!("スレッド{}終了", c);
}

fn main(){
	let th1 = thread::spawn(|| printch('A'));
	let th2 = thread::spawn(|| printch('B'));
	let th3 = thread::spawn(|| printch('C'));

	th1.join().unwrap();
	th2.join().unwrap();
	th3.join().unwrap();
	println!("プログラム終了");
}

### クロージャー化
スレッドを起動するためには無名関数は次のように呼び出す
thread::spawn(|| {
});

use std::thread;
use std::time::Duration;

fn main(){
	let th1 = thread::spawn(|| {
		println!("スレッド1スタート");
		for _i in 1..10 {
			print!("A");
			thread::sleep(Duration::from_millis(100));
		}
		println!("スレッド1終了");
	});
	let th2 = thread::spawn(|| {
		print!("スレッド2スタート");
		for _i in 1..10 {
			print!("B");
			thread::sleep(Duration::from_millis(100));
		}
		println!("スレッド2終了");

	});

	th1.join().unwrap();
	th2.join().unwrap();
	println!("プログラム終了");
}
use std::thread;
use std::time::Duration;

fn main(){
	let func = |c| {
		for _i in 1..10 {
			print!("{}", c);
			thread::sleep(Duration::fromm_millis(100));
		}
	}

	let th1 = thread::spawn(move || func("A"));
	let th2 = thread::spawn(move || func("B"));

	th1.join().unwrap();
	th2.join().unwrap();
	println!("プログラム終了");
}

Rust モジュール

fn three_times(x: i32) -> i32 {
	x * 3
}

fn main() {
	
	for i in 1..4 {
		println!("{}の3倍={}", i, three_times(i));
	}
}

//modsample/src/myutil.rs

pub fn three_times(x: i32) -> i32 {
	x * 3
}
mod myutil;

fn main() {
	
	for i in 1..4 {
		println!("{}の3倍={}", i, myutil ::three_times(i));
	}
}

### ライブラリ
独立したライブラリの作成
$ cargo new mysamplelib –lib

pub fn half(x: i32) -> i32 {
	x / 2
}
use mysamplelib;

fn main() {
	let x = 12;
	let y = mysamplelib::half(x);
	println!("{}の半分は{}", x, y);
}
[package]
name="testmylib"
version = "0.1.0"
authors = ["notes"]
edition = ""

reference/manifest.html

[dependencies]
mysamplelib = {path = "../mysamplelib"}

Rust ライブラリ

Rustでも標準で誰もが使うものが用意されている。ライブラリに相当するものをクレート(crate)と呼ぶことがある
基本的なstdにはプリミティブなデータ型やモジュール、マクロなどが定義されている
crates.ioにたくさんのパッケージがある

let f:f64 = 3.45;
let val = f.sqrt();

use std::f64;

fn main() {
	let f:f64 = 3.45;
	println!("{}の平方根={:?}", f, f.sqrt());
}
fn main() {
	let s = "hello, dogs";
	println!("{:?}", s.to_uppercase());
}

乱数はRngというトレイトを使うことができる

use rand::Rng;

fn main() {
	let mut r = rand::thread_rng();
	for _i in 1..6 {
		println!("{}", r.gen_range(1, 11));
	}
}

### クレートを使うプロジェクト
クレートの最新バージョンを調べる
$ cargo search rand
Updating crates.io index
rand = “0.8.5” # Random number generators and other randomness functionality.
tinyrand = “0.5.0” # Lightweight RNG specification and several ultrafast implementations in Rust.
bevy_rand = “0.1.0” # A plugin to integrate rand for ECS optimised RNG for the Bevy game engine.
random_derive = “0.0.0” # Procedurally defined macro for automatically deriving rand::Rand for structs and enums
faker_rand = “0.1.1” # Fake data generators for lorem ipsum, names, emails, and more
rand_derive2 = “0.1.21” # Generate customizable random types with the rand crate
fake-rand-test = “0.0.0” # Random number generators and other randomness functionality.
ndarray-rand = “0.14.0” # Constructors for randomized arrays. `rand` integration for `ndarray`.
rand_derive = “0.5.0” # `#[derive(Rand)]` macro (deprecated).
rand_core = “0.6.4” # Core random number generator traits and tools for implementation.
… and 1126 crates more (use –limit N to see more)

$ cargo new random3 –bin
Created binary (application) `random3` package

実数の数値計算
abs(), acos(), asin(), atan(), ceil(), copisign(), cos(), exp(), exp2(), floor(), fract(), ln(), log(), log10(), log2(), mul_add(), powf(), powi(), round(), signum(), sin(), sqrt(), tan(), trunc()

use std::f64;

fn main() {
	let f:f64 = 3.45;
	println!("{:?}", f.fract());
}

文字列の操作
find(), matches(), parse(), repeat(), to_lowercase(), to_uppercase(), trim(), trim_end(), trim_start()

乱数の生成
gen(), gen_range(), gen_bool(), gen_ratio(2, 3)

use rand::{thread_rng, Rng};

fn main() {
	let mut rng = thread_rng();

	for _ in 0..11 {
		let x: u16 = rng.gen();
		print!("{} ", x);
	}
}
use rand::{thread_rng, Rng};

fn main() {
	let mut rng = thread_rng();

	for _ in 0..11 {
		let x: u16 = rng.gen_range(0, 3);
		print!("{} ", x);
	}
}

### 日付日時
Date, DateTime, Duration

use chrono::{Utc, Local, DateTime};
use chrono::{Datelike, Timelike};

fn main() {
	let utc: DateTime<Utc> = Utc::now();
	println!("{}", utc);

	let local: DateTime<Local> = Local::now();
	println!("{}", local);

	println!("{}/{}/{}", local.year(), local.month(), local.day());
	println!("{:02}:{:02}:{:02}",
		local.hour(), local.minute(), local.second());
}
use std::thread;
use std::time::Duration;

fn main() {
	println!("3秒待ちます。");
	thread::sleep(Duration::from_secs(3));
	println!("3秒待ちました");
}