Rust所有権と参照

リテラルはプログラム内にハードコードされる

fn main(){
	let msg = "Hello!";
	let msg2 = msg;
	println!("{}", msg);
	println!("{}", msg2);
}

さまざまな値の所有権
L 所有権が移動するため、エラーになる

fn main(){
	let msg = String::from("Hello!");
	let msg2 = msg;
	println!("{}", msg);
	println!("{}", msg2);
}

数値の場合は参照している

関数の所有権

fn main(){
	let msg = String::from("Hello!");
	print_msg(msg);
	printl!("msg: {}", msg);
}

fn print_msg(msg:String) {
	println!("Message is {}", msg);
}

戻り値を設定して渡した関数から返してもらう

fn main(){
	let mut msg = String::from("Hello!");
	msg = print_msg(msg);
	println!("msg: {}", msg);
}

fn print_msg(msg:String)->String {
	println!("Message is \"{}\".", msg);
	msg
}

### 参照
値がある場所を示す
参照は利用した後にドロップされて破棄される

fn main(){
	let msg = String::from("Hello!");
	print_msg(&msg);
	println!("msg: {}", msg);
}

fn print_msg(msg:&String) {
	println!("Message is \"{}\".", msg);
}
fn main(){
	let msg = &String::from("Hello!");
	println!("msg: {}", msg);
	{
		let msg = print_msg(msg);
		println!("msg: {}", msg);
	}
	println!("msg: {}", msg);
}

fn print_msg(msg:&String)->String {
	let msg = String::from("*** ") + msg + " ***";
	println!("Message is \"{}\".", msg);
	msg
}

書き換え可能な値を参照で渡すことで参照した値を変更できます。

fn main(){
	let mut msg = String::from("Hello!");
	println!("msg: {}", msg);
	print_msg(&mut msg);
	println!("msg: {}", msg);
}

fn print_msg(msg:&mut String){
	msg.push_str("!!!!");
	println!("Message is \"{}\".", msg);
}

### スライス

fn main(){
	let msg = "Hello, world!";
	let world = &msg[7..12];
	println!("`{}` in `{}`.", world, msg);
}

可変文字列からのスライス

fn main(){
	let mut msg = String::from("Hello, world!");
	let world = &msg[7..12];
	println!("`{}` in `{}`.", world, msg);
	msg.insert_str(7, "RUST?");
	println!("`{}` in `{}`.", world, msg);
}

スライスを元に可変テキストを作る

fn main(){
	let mut msg = String::from("Hello, world!");

	let world = &msg[7..12];
	println!("`{}` in `{}`.", world, msg);
	msg.insert_str(7, "RUST?");
	
	let mut world = String::from(&msg[7..12]);
	world.push('!');
	println!("`{}` in `{}`.", world, msg);
}

配列スライス

fn main(){
	let data = [12,34, 56,78, 90];
	let part = &data[2..4];
	println!("{:?} in {:?}", part, data);
}
fn main(){
	let mut data = vec![12,34, 56,78, 90];
	let part = &data[2..4];
	data.insert(1, 999);
	println!("{:?} in {:?}", part, data);
}
fn main(){
	let mut data = vec![12,34, 56,78, 90];
	let mut part = data[2..4].to_vec();
	data.insert(1, 999);
	part.push(-1);
	println!("{:?} in {:?}", part, data);
}

Rust関数定義

fn main() {
	hello(String::from("taro"));
	hello(String::from("hanako"));
}

fn hello(name:String) {
	println!("Hello, {}!",name);
}

戻り値を持つ関数

fn main(){
	print_msg(100);
	print_msg(200);
	print_msg(300);
}

fn print_msg(max:i32){
	println!("{}までの合計は、{}です。", max, calc(max));
}

fn calc(max:i32)-> i32 {
	let mut result = 0;
	for n in 0..max {
		result += n;
	}
	result
}

### 値としての関数
定義に関数名がない匿名関数
変数 = |引数| {…処理…}

fn main(){
	let calc = |max| {
		let mut result = 0;
		for n in 0..max {
			result += n;
		}
		result
	};

	let print_msg = |max| {
		println!("{}までの合計は{}です。", max, calc(max));
	}

	print_msg(100);
	print_msg(200);
	print_msg(300);
}

### クロージャ
値として定義された匿名関数を変数に代入したものはクロージャという
元々、関数をオブジェクトとして利用できるようにする仕組み

fn main(){
	let max = 100;
	let res = calc(max);

	let print_msg = || {
		println!("{}までの合計は{}です。", max, res);
	};
	print_msg();

	let max = 200;
	let res = calc(max);
	let print_msg = || {
		println!("0-{} Total:{}", max, res);
	};
	print_msg();
}

fn calc(max:i32)-> i32 {
	let mut result = 0;
	for n in 0..max {
		result += n;
	}
	result
}

Rust複合型とコレクション

### 配列

fn main() {
	let data = [12, 34, 56, 78, 90];
	let mut ans = 0;
	for item in data {
		ans += item;
	}
	println!("データの合計は、{}です。", ans);
}

配列の型は[型;個数]となる。

### タプル

fn main() {
	let taro = ("Taro", 39, true);
	let hanako = ("Hanako", 28, false);
	println!("{:?}", taro);
	println!("{:?}", hanako);
	println!("name:{}, {}", taro.0, hanako.0);
	println!("age:{}, {}", taro.1, hanako.1);
	println!("male?:{}, {}", taro.2, hanako.2);
}

### ベクター型
同じ型の値を多数保管
空のVecを作成: Vec::new()
初期値を持つvecを作成: vec![値1, 値2, …]
値を取り出す: [番号], .get(番号), .get(番号).unwrap()
値を追加: .push(値)

fn main(){
	let mut data = Vec::new();
	data.push(123);
	data.push(456);
	data.push(789);
	println!("0:{}, 1:{}, 2:{}", data[0], data[1], data.get(2).unwrap());
}

### Vecの値を繰り返し処理する
ベクターはコレクションである

fn main(){
	let data = vec![123, 456, 789];
	let mut result = 0;
	for item in data {
		result += item;
	}
	println!("データの合計は、{}です。", result);
}

途中に値を挿入したい場合はinsertを使う
.insert(番号、値)

fn main(){
	let mut data = vec![123, 456, 789];
	data.remove(1);
	data.insert(2, 100);
	println!("{:?}", data);
}

### 名前で値を管理するHashMap
std::collectionsというモジュールで管理されている
use std::collections::HashMMap;

HashMapの作成:HashMap::new();
値を追加: .insert(名前, 値);
値を削除: .remove(名前);

use std::collections::HashMap;

fn main(){
	let mut map = HashMap::new();
	map.insert(String::from("first"), 123);
	map.insert(String::from("second"), 456);
	map.insert(String::from("third"), 789);
	map.remove("second");
	println!("{:?}", map);
}

HashMapは順不同、テキストリテラルではなくStringでテキストを指定
.get(名前).unwrap();

use std::collections::HashMap;

fn main(){
	let mut map = HashMap::new();
	map.insert(String::from("first"), 123);
	map.insert(String::from("second"), map["first"] * 2);
	map.insert(String::from("third"), map.get("first").unwrap() + map.get("second").unwrap());

	println!("{:?}", map);
}

HashMapの繰り返し処理
L for(ky, val) in map

use std::collections::HashMap;

fn main(){
	let mut map = HashMap::new();
	map.insert(String::from("first"), 123);
	map.insert(String::from("second"), 456);
	map.insert(String::from("third"), 789);
	let mut result = 0;
	for (ky, val) in map {
		println!("{}: {}.", ky, val);
		result += val;
	}

	println!("total: {}.", result);
}

### String型のテキスト

fn main() {
	let s1 = String::new();
	let s2 = String::from("Hello");
	let s3 = "World";
	let s4 = s1 + &s2 + &s3;
	println!("{}", s4);
}
fn main() {
	let mut s1 = String::new();
	s1.push_str("Hello");
	s1.push_str("World!");
	println!("{}", s1);
}
fn main() {
	let mut s1 = String::from("Hello,World!");
	s1.insert_str(6, " Rust ");
	s1.insert(7, '*');
	s1.insert(12, '*');
	s1.remove(5);
	println!("{}", s1);
}

### 文字の部分取得
レンジを使って文字列の中から一部分だけ取り出せる

fn main() {
	let s1 = String::from("Hello,Rust World!");
	let s2 = &s1[0..5];
	let s3 = &s1[6..10];
	let s4 = &s1[11..16];
	let s5 = String::new() + s4 + s3 + s2;
	println!("{}", s5);
}

Rust制御フロー

if文

fn main(){
	let num = 1234;
	if num % 2 == 0 {
		println!("{}は偶数です。", num);
	} else {
		println!("{}は奇数です。", num);
	}
}

比較演算式は、==, !=, <, <= など else ifによる条件式 [code] fn main(){ let num = 123; if num % 5 == 0 { println!("{}は5で割れます。", num); } else if num % 4 == 0 { println!("{}は4で割れます。", num); } else if num % 3 == 0 { println!("{}は3で割れます。", num); } else if num % 2 == 0 { println!("{}は2で割れます。", num); } else { println!("{}はうまく割れませんでした。", num); } } [/code] [code] fn main(){ let num:u8 = 7; match num { 1 => println!("{}月は、正月です。", num), 2 => println!("{}月は、節分です。", num), 3 => println!("{}月は、ひな祭りです。", num), 4 => println!("{}月は、入学式です。", num), 5 => println!("{}月は、ゴールデンウィークです。", num), 6 => println!("{}月は、梅雨です。", num), 7 => println!("{}月は、夏休みです。", num), 8 => println!("{}月は、お盆休みです。", num), 9 => println!("{}月は、新学期です。", num), 10 => println!("{}月は、ハロウィンです。", num), 11 => println!("{}月は、ブラックフライデーです。", num), 12 => println!("{}月は、クリスマスです。", num), _ => println!("{}月という月はありません。", num) } } [/code] 複数の値を入力できるよう記述も可能 ### loop式 [code] fn main() { let max = 100; let mut ans = 0; let mut count = 1; loop { if count > max { break; } ans += count; count+= 1; } println!("1から{}までの合計は、{}です。", max, ans); } [/code] "+="は代入演算式 ### while式 [code] fn main() { let max = 100; let mut ans = 0; let mut count = 1; while count <= max { ans += count; count+= 1; } println!("1から{}までの合計は、{}です。", max, ans); } [/code] ### for [code] fn main() { let max = 100; let mut ans = 0; for item in 1..=max { ans += item; } println!("1から{}までの合計は、{}です。", max, ans); } [/code] [code] fn main() { let data = [12, 34, 56, 78, 90]; let mut ans = 0; for item in data { ans += item; } println!("データの合計は、{}です。", ans); } [/code]

Rust基礎

### Rustのバージョン確認
$ rustc –version
rustc 1.57.0 (f1edd0429 2021-11-29)

### Rustのアップデート
$ rustup update

### プロジェクトの作成
$ cargo new sammple_rust_app

### Rustの型
主に整数、実数、論理、文字などが用意されている

### 整数型
i8, u8, i16, u16, i32, u32, i64 u64, isize, usize
※符号付き、なしが用意されている。サイズはメモリサイズで、小さい方がメモリを消費しない
※可変はプログラムのプラットフォームの値
特別な理由がなければi32を使用する
浮動小数の型はf32, f64がある。f32は特別な理由がない限り使わない

### 文字
char型: 1文字の型、シングルクォート記号をつける
str型: 一般的なテキスト型、ダブルクォート記号をつける
bool型: true or false
スカラ型(1つの値)とベクター型(動的に値を増減できる)

### 値の演算
– 型の変換はas、テキストの演算は+

### 変数の型指定
let 変数: 型 = 値;

fn main(){
	let x = 100;
	let y:i64 = 200;
	let z = x + y;
	println!("{}+{}={}", x, y, z);
}

“!”はマクロを指す
mutによる可変設定

fn main(){
	let x = 100;
	let y:i64 = 200;
	let mut z = x + y;
	println!("{}+{}={}", x, y, z);
	z = x - y;
	println!("{}-{}={}", x, y, z);
}

変数のシャドーイングという機能を使うと、新たに変数を定義することができる
常に変わらない変数はconstを使って宣言する。rustは定数は大文字で記述する

fn main(){
	const X:i32 = 100;
	const Y:i32 = 200;
	const Z:i32 = X + Y;
	println!("{}+{}={}", X, Y, Z);
}

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));
}