Fromトレイト(@Rust)

https://doc.rust-jp.rs/rust-by-example-ja/conversion/from_into.html

に以下のFromトレイトを使ったユーザ定義型の型変換サンプルコードが出てきますが、お馴染みのString::fromもString型に対して(impl From<&str> for String {~~途中省略~~})とfromを定義しているのでやっていることは同じ、

use std::convert::From;

#[derive(Debug)]
struct Number {
    value: i32,
}

impl From for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}

fn main() {
    let num = Number::from(30);
    println!("My number is {:?}", num);
}

std::convert::Fromトレイトは、

pub trait From: Sized {
    // Required method
    fn from(value: T) -> Self;
}

のようになっているので、from()にこの例のように個別の実装(impl From for Number)をしてやれば、自作の型でも型変換ができるようになります。

 

admin

 

 

refパターン(@Rust)

https://doc.rust-jp.rs/rust-by-example-ja/custom_types/enum/testcase_linked_list.html

のコードでrefと&で何が違うのと思ったのですが、

Rust by exampleの以下のページに出てくるrefパターン

https://doc.rust-jp.rs/rust-by-example-ja/scope/borrow/ref.html

の解説は、

—————————-

 // 左辺に`ref`をつけることによる借用と、右辺に`&`をつけることによる借用は等価

    let ref ref_c1 = c;

    let ref_c2 = &c;


ということなので、どちらも借用ですが似てるようで意味は全く異なります。

該当部分のコード全体をいかに再掲(長ったらしいので日本語は削除)しますが、タプル全体の文字列と長さの取得処理は再帰処理を使っています。

https://doc.rust-jp.rs/rust-by-example-ja/custom_types/enum/testcase_linked_list.html

use crate::List::*; // equivalent to following two lines, it's a short form of the enum List 
//use crate::List::Cons;
//use crate::List::Nil;

#[derive(Debug)]
enum List {
    // Cons: Tuple struct that wraps an element and a pointer to the next node
    Cons(u32, Box 	 	 	),
    // Nil: A node that signifies the end of the linked list
    Nil,
}

// Methods can be attached to an enum
impl List {
    // Create an empty list
    fn new() -> List {
        // `Nil` has type `List`
        Nil
    }

    // Consume a list, and return the same list with a new element at its front
    fn prepend(self, elem: u32) -> List {
        // `Cons` also has type List
        Cons(elem, Box::new(self))
    }

    // Return the length of the list(using recursive call)
    fn len(&self) -> u32 {
        // `self` has to be matched, because the behavior of this method
        // depends on the variant of `self`
        // `self` has type `&List`, and `*self` has type `List`, matching on a
        // concrete type `T` is preferred over a match on a reference `&T`
        // after Rust 2018 you can use self here and tail (with no ref) below as well,
        // rust will infer &s and ref tail. 
        // See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html
        match *self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            Cons(_, ref tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length
            Nil => 0
        }
    }

    // Return representation of the list as a (heap allocated) string(using recursive call)
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` is similar to `print!`, but returns a heap
                // allocated string instead of printing to the console
                //println!("st: {}, {}", head, tail.stringify());
                let ret: String = format!("{}, {}", head, tail.stringify());
                println!("{:?}", ret);
                ret
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // Create an empty linked list
    let mut list = List::new();

    // Prepend some elements
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}

コメントにあるように2018年版のRustからは*selfとref tailの*とrefは不要になった(&とrefを推論する)そうで、確かにそう変えてもコンパイルは通ります。となると、fn stringify(&self)も同じように書き換え可能なので、このソースでrefを使う必要はなくなりますが。

        match self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            Cons(_, tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length

 

admin

モジュール化(@Rust)

コードの記述が一個のファイルに収まるケースというのは現実にはほぼないので、モジュール構成が必要になりますが、以下は割と単純なケースになります。

最初は印字する関数print()だけのファイルmod_sample.rsがmain.rsと同じディレクトリに存在しています。

<mod_sample.rs>

pub fn print(arg: &str) {
    println!("{}", arg);
}

呼び出される関数はpublic(pub)でないとコンパイルエラーになります。

呼び出し側は、

<main.rs>

mod mod_sample;

fn main() {
    use crate::mod_sample::print;
    let msg = "hello";
    print(&msg);
}

Rustの場合にはmain.rs(バイナリクレートが複数あればそれぞれに)にmod文で呼び出すクレート名(mod_sample)を指定しておきます。use文はクレートの短縮形を指定しているだけで、本質ではありません。

一番シンプルにはこれだけですが、実際には呼び出される側が階層構造になっているケースが多いだろうと思われます。例えば以下のようなディレクトリ構成で、

ここで新たに現れるファイルがmod.rsで、これはこの名前でないとモジュールの検索ができません。

mod.rsの中身は他のクレートを定義しているだけですが、mod.rs経由でここで大義されたクレートを呼び出すわけです。呼び出されるmod_sample.rsには変更は不要です。

<mod.rs>

pub mod mod_sample;

クレートファイルが複数存在する場合にはそれらを列挙、最後にmain.rsにも変更が入ります。階層構造のルートディレクトリ名を指定するようにします。

<main.rs>

mod libra;

fn main() {
    use crate::libra::mod_sample::print;
    let msg = "hello";
    print(&msg);
}

 

admin

 

デストラクト(@Rust)

デストラクト(destructuring)は紛らわしいのですが、Rustの変数がスコープを外れた時に廃棄されるデストラクター(destructor)とは別物です。

ではデストラクトとは何かというと、

https://exercism.org/tracks/rust/concepts/destructuring

に記載あるように、タプルや構造体の要素の初期化です。

About Destructuring

Destructuring is the process of breaking down items into their component parts, binding each to smaller variables.

例えばタプルの初期化ならば、

Destructuring Tuples

Destructuring tuples is simple: just assign new variable names to each field of the tuple:

let (first, second) = (1, 2);

というふうに、機能的にはJavaScriptの分割代入 (Destructuring assignment) 構文と同じに見えます。

Rust by exampleでは、

https://doc.rust-lang.org/stable/rust-by-example/flow_control/match/destructuring.html

が公式ドキュメントです。

 

admin

構造体リテラル更新記法におけるString型(@Rust)

構造体のインスタンス作成時に、すでに作成済みのインスタンを使用するということですが、以下の例でdivの型をString型とするとコンパイルが通りません。

何故なら、divをString型にするとemp2で..emp1でdivを使用した瞬間に所有権がemp1からemp2に移動してしまうためです。数値はプリミティブ型なのでそのようなことは起きないのですが。

#[derive(Debug)]
struct Employee<'lt> {
    name: String,
    age: u8,
    grade: u8,
    div: &'lt str,
}

fn main() {
    let div: &str = "sales";
    let emp1 = Employee{name: String::from("John"), age: 30, grade: 3, div: div};
    let emp2: Employee = Employee{name: String::from("Bill"), age: 25, ..emp1};
    println!("{:?}, {:?}", emp1, emp2);
}

つまり構造体でフィールドとして参照を持つ場合、参照フィールドには必ずライフタイムが必要ということです。

以下のようにstatic宣言にしてプログラム実行中のライフサイクルにしても良いですが、永続的なライフタイムを持つすなわちメモリをずっと占有してしまいます。

struct Employee {
    name: String,
    age: u8,
    grade: u8,
    div: &'static str,
}

Rustでは所有権にまつわる規則がどこかで必ず現れてきます。

 

admin

 

 

構造体をclone()するためには(@Rust)

これは以下のRust by exampleに出てくる演習問題に関連しますが、

https://doc.rust-jp.rs/rust-by-example-ja/primitives/tuples.html

Rustでは色々な作法の一つですが、演習1と2を適用するためにclone()を使おうとしましたが、clone()するためには#[derive(Clone)]を該当の構造体の前に追加しないといけません、実際のコードではDebugが存在しているのでそれに追加。この指定をしないと、method `clone` not found for this struct(@22行目)と言われてコンパイルできません。

#[derive]アトリビュートは、特定のtraitを標準実装させる機能です。

行列を入れ替えるだけなら、method使ってもいいですが、それだとimpl fmt::Display for Matrixは機能しません。impl Matrixtとしても、Matrixの型情報は引き継がないようです。
use std::fmt;

// Tuples can be used as function arguments and as return values.
// タプルを関数の引数及び返り値として使用している。
fn reverse(pair: (i32, bool)) -> (bool, i32) {
    // `let` can be used to bind the members of a tuple to variables.
    // `let`でタプルの中の値を別の変数に束縛することができる。
    let (int_param, bool_param) = pair;

    (bool_param, int_param)
}

// using method to transpose
impl Matrix {
    fn transpose(&self) -> (f32, f32, f32, f32) {
        (self.0, self.2, self.1, self.3)
    }
}

// transpose function
fn transpose(tran: Matrix) -> Matrix {
    let mut trans = tran.clone();
    trans.2 = tran.1;
    trans.1 = tran.2;
    trans
}

// The following struct is for the activity.
// 以下の構造体は後ほど「演習」で用いる。
#[derive(Debug, Clone)]
struct Matrix(f32, f32, f32, f32);

fn main() {
    // A tuple with a bunch of different types.
    // 様々な型を値に持つタプル
    let long_tuple = (
        1u8, 2u16, 3u32, 4u64, -1i8, -2i16, -3i32, -4i64, 0.1f32, 0.2f64, 'a', true,
    );

    // Values can be extracted from the tuple using tuple indexing.
    // インデックスを用いて、タプル内の要素を参照できる。
    println!("Long tuple first value: {}", long_tuple.0);
    println!("Long tuple second value: {}", long_tuple.1);

    // Tuples can be tuple members.
    // タプルはタプルのメンバになれる
    let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);

    // Tuples are printable.
    // タプルはプリント可能である。
    println!("tuple of tuples: {:?}", tuple_of_tuples);

    // But long Tuples (more than 12 elements) cannot be printed.
    // しかし長すぎるタプル(12要素より多いもの)はプリントできない
    //let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
    //println!("Too long tuple: {:?}", too_long_tuple);
    // TODO ^ Uncomment the above 2 lines to see the compiler error
    // TODO ^ 上記2行のコメントを外して、コンパイルエラーになることを確認

    let pair = (1, true);
    println!("Pair is {:?}", pair);

    println!("The reversed pair is {:?}", reverse(pair));

    // To create one element tuples, the comma is required to tell them apart
    // from a literal surrounded by parentheses.
    // 要素を1つしか持たないタプルを作成する場合、括弧で囲まれたただのリテラル
    // と区別するため、カンマが必要になる。
    println!("One element tuple: {:?}", (5u32,));
    println!("Just an integer: {:?}", (5u32));

    // Tuples can be destructured to create bindings.
    //タプルを分解して別の変数にそれぞれの値を代入
    let tuple = (1, "hello", 4.5, true);

    let (a, b, c, d) = tuple;
    println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d);

    let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
    println!("{:?}", matrix);

    impl fmt::Display for Matrix {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            writeln!(f, "({} {}) \n ({} {})", self.0, self.1, self.2, self.3)
        }
    }

    println!("Formatted Matrix {}", matrix);

    let tran = matrix.transpose();
    println!("Using method {:?}", tran);

    println!("Transposed Matrix {}", transpose(matrix));
}
admin

nullのRustでの代替え手段(@Rust)

Rustには例外処理もnullもありませんが、これはRustの思想そのもの。例外処理やnullが無い代わりに、その機能をサポートする手段として、enumとしてのResultOptionがあるということになります。

https://doc.rust-lang.org/std/option/

を参照すると、”Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.“であり、Rustでは標準的に使われる機能になります。

とありますが、Optionは実は、以下のようなenumになっていて要素としてはNoneとSome(T)が存在してます。

pub enum Option {
    None,
    Some(T),
}

通例では、Optionはパターンマッチングと一緒に使われて、rust-lang.orgの以下のサンプルコードを見ると、match resultでマッチング処理を行なっています。

fn main(){
   fn divide(numerator: f64, denominator: f64) -> Option {
        if denominator == 0.0 {
            None
        } else {
            Some(numerator / denominator)
        }
    }

    // The return value of the function is an option
    let result = divide(10.5, 1.5);

    // Pattern match to retrieve the value
    match result {
        // The division was valid
        Some(x) => println!("Result: {x}"),
        // The division was invalid
        None => println!("Cannot divide by 0"),
    }
}

Optionに限らずResultも類似のやり方で処理されます。

いずれにしてもRust固有の記法というように見えます。

 

admin

マクロがどう展開されているのかを調べる(@Rust)

println!とかvec!はRustにおける頻繁に使われるマクロですが、マクロは多くの言語が持つ機能でそれはコンパイル時には展開されます。じゃRustのマクロがどのように展開されるか?を見るためのツールにcargo expandがあります。

https://crates.io/crates/cargo-expand

cargo initで作成されるmain.rsで見てみます。

<ソース>

fn main() {
    println!("Hello, world!");
}

マクロを含んだソースコードを対象にするならば、*.rsファイルの存在するディレクトリでcargo expandを実行します。

<展開後>

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    {
        ::std::io::_print(format_args!("Hello, world!\n"));
    };
}

展開されたコード中には、さらにformat_args!というマクロが見えています、まあマクロからマクロは呼び出しできるから。

format_argsマクロを検索すると、以下のように記述されています。

https://doc.rust-lang.org/std/macro.format_args.html

macro_rules! format_args {
    ($fmt:expr) => { ... };
    ($fmt:expr, $($args:tt)*) => { ... };
}

という事で、引数がいくつかある場合にそれを展開してprint形式を作り出すマクロです。

 

実はcargo expandはパラメタ指定でソースコードではなくてバイナリからもマクロが展開されたソースコードの出力ができます。ソースコードからexpandしたものと全く同じに見えますが。

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    {
        ::std::io::_print(format_args!("Hello, world!\n"));
    };
}

使い道はカスタムに作られたマクロのソースコード確認用になるでしょうか。

 

admin

スレッド間通信をchannelからmutexへ(@Rust)

メモリ管理を厳密に実行できるRustではスレッド間通信にmutexを使うのも現実的のように思います。おそらくchannelよりは軽量だろうと想像されるから。

https://doc.rust-jp.rs/book-ja/ch16-03-shared-state.html

を参考にchannel版からmutex版に書き換えてみます。

https://isehara-3lv.sakura.ne.jp/blog/2023/10/10/スレッド間通信でchannelを使うrust/

肝は追加されたクレートのMutexとArcになるでしょう。Rcはスレッドセーフではないので多少処理は遅くなるけれどもArcが用意されているようです。

以下のコードではchannel版をコメントアウトしてmutexに置き換えています。

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

fn main() {
    //let (tx, rx) = mpsc::channel();
    let count = Arc::new(Mutex::new(0));
    let mut handles = Vec::new();

    for i in 0..3 {
        //let thread_tx = tx.clone();
        let count = Arc::clone(&count);
        let handle = thread::spawn(move || {
            for j in 1..10 {
                println!("hi number {} {} from the spawned thread!", i, j);
                thread::sleep(Duration::from_millis(1));
            }
            //thread_tx.send(i).unwrap();
            let mut num = count.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    //drop(tx);       // without drop(), this program doesn't terminate, wait for tx forever
/* 
    for received in rx {
        println!("Got from the threads : {}", received);
    }
*/
    println!("Result: {}", *count.lock().unwrap());

}

mutexで定義した変数の型は整数型ではない(VScode上では:Arc<Mutex[i32; 1]>>と表示されている)ので、unwrap()してやらないといけないのはRust特有ですが、Rustのスレッド間通信(共有)でmutexも選択肢に入りそうです。

 

admin

 

 

 

スレッド間通信でchannelを使う(@Rust)

Rustの場合にはchannelを使わなくても、メモリ管理がきちんとされているのでmutexでも良さそうなのですが、channelの方が使うのは簡単だと思う。ほぼGolangと同等の機能ですが、rx/txを同時に定義するのは異なります。Golangは -> もしくは <- でインアウトを切り替えるし、channelのサイズも指定しますが、RustではFIFOのように動作します。

以下のコードは、

https://isehara-3lv.sakura.ne.jp/blog/2023/10/06/並行処理rust/

にスレッド起動後にスレッドの番号をchannelに送るようにしたもの。一点注意すべきはtxはmainスレッド中で生成されているので、for received in rx処理はdrop(tx)しないと無限待ちになること。もしクロージャー中ならば、そのライフが終わった時点でdrop()されますが。

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

fn main() {
    let (tx, rx) = mpsc::channel();
    let mut handles = Vec::new();
    for i in 0..3 {
        let thread_tx = tx.clone();
        let handle = thread::spawn(move || {
            for j in 1..10 {
                println!("hi number {} {} from the spawned thread!", i, j);
                thread::sleep(Duration::from_millis(1));
            }
            thread_tx.send(i).unwrap();
        });
        handles.push(handle);
    }

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    drop(tx);       // without drop(), this program doesn't terminate, wait for tx forever

    for received in rx {
        println!("Got from the threads : {}", received);
    }

}

出力の最後部分だけ、

~~~~~~
hi number 0 9 from the spawned thread!
hi number 2 9 from the spawned thread!
Got from the threads : 0
Got from the threads : 1
Got from the threads : 2

P.S. 同じスレッド間での通信になっていたので修正(2023/10/11)

送信エンドポイントはclone()しないとコンパイルが通りません(moveしたものをdrop()はできないから)。clone()して複数の送信エンドポイントから送信でもrxに集約されるようです。

 

admin