Open23

Rustのメモ

scirexsscirexs

文字数取得には.chars().count()を使うと良さそう。
空文字判定には.is_empty()を使う。
参考文献: Rustで文字列の長さ(文字数)を取得

fn main() {
    let ascii = "Foo!";
    let emoji = "😇🙌";
    let grapheme = "🇯🇵🇺🇸";

    assert_eq!(ascii.len(), 4); // バイト数を取得
    assert_eq!(emoji.chars().count(), 2); // 書記素クラスタを考慮しない文字数取得
    // required "unicode-segmentation" crate
    use unicode_segmentation::UnicodeSegmentation;
    assert_eq!(grapheme.graphemes(true).count(), 2); // 書記素クラスタを考慮した文字数取得
}
scirexsscirexs

トレイトを用いて型パラメータに制約を与える以下のprint_field1,2,3の定義は全て同じ意味。
参考文献: Rust: ジェネリクスとトレイト

fn print_field1(foo: &impl GetTrait) {
    println!("{}", foo.get_field());
}
fn print_field2<T: GetTrait>(foo: &T) {
    println!("{}", foo.get_field());
}
fn print_field3<T>(foo: &T)
    where T: GetTrait // 複数の場合はカンマ区切りで書く T: GetTrait, U: SetTrait
{
    println!("{}", foo.get_field());
}

fn main() {
    let foo = Foo {bar: 0};
    print_field1(&foo);
    print_field2(&foo);
    print_field3(&foo);
}

struct Foo {
    bar: i32
}
trait GetTrait {
    fn get_field(&self) -> i32;
}
impl GetTrait for Foo {
    fn get_field(&self) -> i32 {
        self.bar
    }
}
scirexsscirexs

トレイトは型に対して実装できる。

struct Foo { f1: i32, f2: String } // フィールド型構造体
struct Bar(i32, String); // タプル型構造体
struct Baz; // ユニット型構造体
enum Qux { V1, V2 }
enum Quux {}
type Corge = fn() -> Option<i32>;

trait Grault {
    fn get();
}
impl Grault for Foo {
    fn get() { }
}
impl Grault for Bar {
    fn get() { }
}
impl Grault for Baz {
    fn get() { }
}
impl Grault for Qux {
    fn get() { }
}
impl Grault for Quux {
    fn get() { }
}
impl Grault for Corge {
    fn get() { }
}
impl Grault for i32 {
    fn get() { }
}
scirexsscirexs

構造体は一部のフィールドのみ所有権を移動することもできる。

fn main() {
    let foo = Foo { f1: "field1".to_string(), f2: 2 };
    let bar = foo.f1;
    println!("{}", bar);
    // println!("{}", foo.f1); // 所有権移動済みのためエラー
    let baz = foo.f2;
    println!("{}", baz);
    println!("{}", foo.f2); // Copyされたため有効
    // println!("{:?}", foo); // 部分的に所有権移動済みのためエラー
}
#[derive(Debug)]
struct Foo {
    f1: String,
    f2: i32,
}
scirexsscirexs

2重にforループを書く代わりにitertoolsが使える場合がある。

use itertools::Itertools;
fn main() {
    let mut foo = Vec::new();
    for i in 0..5 {
        for j in 1..6 {
            foo.push(i);
            foo.push(j);
        }
    }
    let mut bar = Vec::new();
    for (i, j) in (0..5).cartesian_product(1..6) {
        bar.push(i);
        bar.push(j);
    }
    assert_eq!(foo, bar);
}
scirexsscirexs

selfはある型のインスタンスを表現する。
Selfはある型を表現する。

scirexsscirexs

ジャグ配列様のVecから一番要素数の多いVecのインデックスを特定する。
参考文献: Rustでmax_indexを取得する

fn main() {
    let foo = vec![vec![1,2], vec![3,4,5,6], vec![7,8,9]];
    let (index, _) = foo.iter()
        .map(|x| x.len())
        .enumerate()
        .fold((usize::MIN, usize::MIN), |(acc_i, acc_l), (i, l)| if acc_l>l {(acc_i, acc_l)} else {(i, l)});
    if let Some(x) = foo.get(index) {
        println!("{:?}", x); // [3, 4, 5, 6]
    }
}
scirexsscirexs

演算子オーバーロードの例

// + -> Add::add
// += -> AddAssign::add_assign
// - -> Sub::sub
// * -> Mul::mul
// / -> Div::div
// % -> Rem::rem

fn main() {
    let foo = Foo{ f1: 12, f2: 34 };
    let bar = Bar{ f1: 56, f2: 78 };
    println!("{:?}", foo + foo);  // Foo { f1: 24, f2: 68 }
    println!("{:?}", foo + bar);  // Foo { f1: 90, f2: 90 }
}

#[derive(Clone, Copy, Debug)]
struct Foo { f1: i32, f2: i32 }
impl std::ops::Add<Foo> for Foo {
    type Output = Foo;
    fn add(self, rhs: Foo) -> Self::Output {
        Self::Output{ f1: self.f1 + rhs.f1, f2: self.f2 + rhs.f2 }
    }
}
impl std::ops::Add<Bar> for Foo {
    type Output = Foo;
    fn add(self, rhs: Bar) -> Self::Output {
        Self::Output{ f1: self.f1 + rhs.f2, f2: self.f2 + rhs.f1 }
    }
}
struct Bar { f1: i32, f2: i32 }
scirexsscirexs

簡単なiter可能かどうかの考え方

  • 同じ型が連なる構造かどうか
    • 可能: 配列やVec, Hashmap
    • 不可能: タプルや構造体
scirexsscirexs

文字列のアドレス

fn main() {
    let a = "str";
    let b = &a[..];
    let c = &a[1..];
    let d = a.to_string();
    let e = &d;
    let f = &b[..];
    let g = &b[1..];
    println!("{:p}", a); // static 's'のアドレス
    println!("{:p}", b); // static 's'のアドレス
    println!("{:p}", c); // static 't'のアドレス
    println!("{:p}", d.as_ptr()); // heap struct Stringのアドレス
    println!("{:p}", e); // stack Stringのアドレス
    println!("{:p}", f); // heap Vec<u8>の第一要素アドレス
    println!("{:p}", g); // heap Vec<u8>の第二要素アドレス
}
scirexsscirexs

複合型(データ構造)の内容をただの変数に所有権を移動させる方法

fn main() {
    let foo_int = Box::new(1);
    let bar_int1 = Box::new(2);
    let bar_int2 = Box::new(3);
    let baz_int = Box::new(4);
    let qux_int1 = Box::new(5);
    let qux_int2 = Box::new(6);
    let quux_int1 = Box::new(7);
    let quux_int2 = Box::new(8);

    // 用意したヒープの値を用いてデータ構造作成
    let foo1 = foo_int;
    let bar1 = vec![bar_int1, bar_int2];
    let baz1 = Baz::V1(baz_int);
    let qux1 = Qux { f1: qux_int1, f2: qux_int2 };
    let quux1 = (quux_int1, quux_int2);

    // 所有権がデータ構造に移動していることを確認 Error "value borrowed here after move"
    // println!("{}", foo_int);
    // println!("{}", bar_int1);
    // println!("{}", bar_int2);
    // println!("{}", baz_int);
    // println!("{}", qux_int1);
    // println!("{}", qux_int2);
    // println!("{}", quux_int1);
    // println!("{}", quux_int2);

    // データ構造から内容の所有権をただの変数に取り戻す
    let foo = foo1;
    println!("last foo_int: {}", foo);
    for (i, bar) in bar1.into_iter().enumerate() {
        println!("last bar_int{}: {}", i+1, bar);
    }
    if let Baz::V1(baz) = baz1 {
        println!("last baz_int: {}", baz);
    }
    let Qux {f1: qux, f2: _} = qux1; // 構造体の一部だけ所有権移動できる
    println!("last qux_int1: {}", qux);
    println!("last qux_int2: {}", qux1.f2);
    let (quux, _) = quux1;           // タプルも構造体と同じ
    println!("last quux_int1: {}", quux);
    println!("last quux_int2: {}", quux1.1);

    // 所有権が移動していることを確認 Error "value borrowed here after move" 等
    // println!("{}", foo1);
    // println!("{:?}", bar1);
    // println!("{:?}", baz1);
    // println!("{}", qux1.f1);
    // println!("{}", quux1.0);
}

#[derive(Debug)]
enum Baz { V1(Box<i32>) }
#[derive(Debug)]
struct Qux { f1: Box<i32>, f2: Box<i32> }
scirexsscirexs

構造体のフィールドの値をCopy無しで入れ替える
std::mem::replaceの代わりにstd::mem::takeも使用可能(takedefaultと入れ替える)

fn main() {
    let mut foo = Foo::new();
    foo.replace_vec(); // ab
    assert_eq!(foo.f1, vec!["c".to_string()]);
}

struct Foo {
    f1: Vec<String>,
}
impl Foo {
    fn new() -> Self {
        Foo { f1: vec!["a".to_string(), "b".to_string()] }
    }
    fn replace_vec(&mut self) {
        std::mem::replace(&mut self.f1, vec!["c".to_string()])
            .into_iter()
            .for_each(|x| print!("{}", x));
        println!();
    }
}
scirexsscirexs

OptionVecのスマートな取り扱い例

fn main() {
    let opt1: Option<i32> = None;
    let opt2: Option<i32> = Some(9);
    let mut nums: Vec<i32> = vec![1,2,3];
    nums.extend(opt1);
    nums.extend(opt2);

    for n in nums {
        print!("{}", n); // 1239
    }
}
scirexsscirexs

パターン記法色々

struct Foo { f1: i32, f2: i32 }
fn main() {
    let foo = Foo { f1: 1, f2: 2 };
    let bar = (3,4,5,6,7,8,9);
    let baz = Some(3);
    let qux: Result<String, String> = Ok("id2".to_string());

    match foo {
        Foo { f1: _x, f2: 3 } => unreachable!(),
        Foo { f1: 0|1|2, f2: y } => assert_eq!(y, 2),
        _ => unreachable!(),
    }
    match bar {
        (0, .., _last) => unreachable!(),
        (3, second, ..) => assert_eq!(second, 4),
        _ => unreachable!(),
    }
    match baz {
        Some(x) if x < 2 => unreachable!(),
        Some(ref x @ 3..=7) => assert_eq!(*x, 3), // not take value
        _ => unreachable!(),
    }
    if let Some(x @ 3) = baz { assert_eq!(x, 3); }

    match qux {
        Ok(x) if x == "id1" => { println!("match id1"); },
        Ok(x) if x == "id2" => { println!("match id2"); },
        Ok(x) if x == "id3" => { println!("match id3"); },
        Ok(_) => {  println!("Ok others"); },
        _ => { println!("Error"); },
    }
    // match id2
}
scirexsscirexs

ジェネリクスの文脈でPhantomDataを使用するときは、所有に関する処理や自動トレイトの制御を避けるためにPhantomData(T)ではなくPhantomData<fn(T)>を用いる。
参考文献: PhantomDataまとめ

scirexsscirexs

環境変数取得: std::env::var(var_name) -> String (envyクレートもある)
起動引数取得: std::env::args() -> Args (ArgsはIntoIter<String>のようなもの)