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.
パーサーを書いてみたが パーサーとモデルの切り分けを コールバックを使って うまくできた。