LoginSignup
1
2

More than 5 years have passed since last update.

3文字パーサーで グローバル変数、コールバック関数、関数のようなものを返す関数 を練習

Last updated at Posted at 2018-08-05

Rustで、グローバル変数を更新する似たようなコールバック関数を複数個作る書き方の備忘メモ。

// Do it: Cargo.toml を見る。
// ### 追加例
// [dependencies]
// lazy_static = "1.0"

/// 3文字パーサー
///
/// ### 入力例
/// ABC
///
/// ### 出力例
/// A
/// B
/// C
/// Count: 7
///
/// 最後のカウントはプログラムで適当に足し算したもの。

// グローバル変数を作るのに使う。
#[macro_use]
extern crate lazy_static;


// グローバル変数にしたいものは、読み書きロックに入れる。
// 使い方
//
// ### 内容に変更ないとき。グローバル変数を読込みモードでロックし、その中身の count 値を取得する。
// let num = APPLE_WRAP.read().unwrap().count;
//
// ### 内容を変更するとき。グローバル変数を書き込みモードでロックし、その中身の .add メソッドを使う。
// APPLE_WRAP.write().unwrap().add(4);
use std::sync::RwLock;
lazy_static! {
    static ref APPLE_WRAP: RwLock<Apple> = RwLock::new(Apple::new());
}


/// 数えるだけ。
pub struct Apple{
    pub count:i8
}
impl Apple{
    pub fn new()->Apple{
        Apple{
            count: 0
        }
    }
    pub fn add(&mut self, number:i8){
        self.count += number;
    }
}


/// 関数を返す関数
fn create_program() -> fn(text:String) {
    |text|{
        println!("{}",text);
        APPLE_WRAP.write().unwrap().add(4);
    }
}


// このアプリケーションのスタート地点。
fn main() {
    let mut parser = Parser::new("ABC".to_string());

    let banana = |text|{
        println!("{}",text);
        APPLE_WRAP.write().unwrap().add(1);
    };

    // 1文字目。
    parser.parse1(banana);

    // 2文字目。
    parser.parse1(create_program());
    APPLE_WRAP.write().unwrap().add(2);

    // 3文字目。
    parser.parse();

    println!("Count: {}", APPLE_WRAP.read().unwrap().count);
}



/// コールバック関数。
type Callback = fn(character:String);
/// パーサー。
pub struct Parser{
    text:String,
    start:usize,
    end:usize
}
impl Parser{
    pub fn new(text:String)->Parser{
        Parser{
            text:text,
            start:0,
            end:1
        }
    }

    pub fn parse(&mut self){
        println!("{}",&self.text[self.start..self.end]);
        self.start+=1;
        self.end+=1;
    }

    pub fn parse1(&mut self, callback:Callback){
        callback(self.text[self.start..self.end].to_string());
        self.start+=1;
        self.end+=1;
    }
}

大きなプログラムで動かすと デッドロックを起こしてしまう。 うーむ。
以下のように 気を使って書けば 避けれているような気はする。

// ### 気を使う前。
let peaches = PEACHES_WRAP.try_read().unwrap();
let peach = peaches[0];
eat(&peach);// この中でPEACHES_WRAPグローバル変数を使っていたら突合してしまう。

// ### 気を使った後。
let peach;
{
    let peaches = PEACHES_WRAP.try_read().unwrap();
    peach = peaches [0];
}// 閉じ括弧でグローバル変数のスコープは終わる。
eat(&peach);

コールバック関数のりくつ

    let func = |grape_juice|{
        let mut banana = BANANA_WRAP.try_write().unwrap();
        banana.set_grape_juice(grape_juice);
    };
    func(mov);

関数も、変数のように作れる。
Rust はコールバック関数を作りやすい。
詳しくはドキュメントを読む。

以下のように関数を引数にするだけでは、関数は渡せるんだが、クロージャではないので 関数の外側の変数が見れなかったりする。

pub fn grate_func(juice_mixer: fn(Juice)) {
    let juice = Juice::new();
    juice_mixer(juice);
}

クロージャは次のように書く。 Fn と FnMut は使い分ける。

pub fn grate_func<F1>(mut juice_mixer: F1)
    where F1 : FnMut(Juice)
{
    let juice = Juice::new();
    juice_mixer(juice);
}

Examples.

パーサーを書いてみたが パーサーとモデルの切り分けを コールバックを使って うまくできた。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2