ダーツとさび:async物語🔃
最後のブログ記事では、両方の言語を接続し、より安全な、パフォーマンスのフラッターアプリケーションを構築する方法を示します.
このブログ記事では、マルチスレッドAsync RustコードをAsync DARTで使用する方法と、1つ以上のランタイムを持つアプリケーションをどのように構築するかについて話しますsōzu 作品
Asyncのループを実行すると、常にそれを取得するのは簡単ではない、あなたはそれがDART VMが独自のAsyncのランタイムであり、錆では、我々は人気のランタイムのいずれかをプラグインすることができることを知っているTokio , async-std , 最近smol これは、低レベルのランタイムとして、async - stdを内蔵しています.
問題は、言語としてのDARTはシングルスレッドであるように設計されているということですが、それ自体は問題ではありませんが、後にどのようにRustを使うときに問題になるでしょう.
さて、あなたは以上を実行することができますIsolate これは、彼らが一緒に動作し、メッセージを渡すことによって通信する小さなダーツVMのインスタンスのようなものです、ここではフラッターチームからのアンドリューは、DARTの分離とイベントループについての話は、私は非常にダーツ分離についての考えがない場合は😅)
このポストをよりエキサイティングにするために、単純なウェブをさびている図書館をこすって、フラッタアプリケーションでそれを使ってください.
両方の言語を接続する方法についての最後のブログの投稿からの経験を使用して、我々もここで同じ考えを使用します.
最初に錆木箱を作成します
次に、FFIバインディングを書き込みます
我々は選択しますTokio 私たちのランタイムとしては、どのようなランタイムを使用するかは問題ではありません
also lazy_static それは1秒で便利でしょう.
ffiの間のエラーを処理するのは難しいので、我々は我々の人生を容易にするヘルパー枠を使用します.
追加ffi_helpers 依存する
今、問題は、どのように我々は露出する
つの方法は、通常の非
もう一つの方法があります
良い、それはオプションとJavaScriptを使用する
理論的には動作しますが
前に言ったように、DARTはシングルスレッドであるように設計されているので、他のスレッドからコールバックを呼び出すことはできませんhere ).
では、解決策は何でしょうか?
私があなたに言ったならば、我々はDART
すべての新しい分離SendPort and ReceivePort これらは
我々がこれらの数を我々のものに使うならば、我々
allo-isolate 我々が築いた木箱ですSunshine DART VMと一緒にマルチスレッドの錆を実行するのを助けるために🌀
それは必要
フードの下では、DART APIを使用します
私たちの例に戻ると、我々は今どのように公開する方法を知っている
もし私があなたがCヘッダーファイルからあなたのダーツFCI結合を生成することができたと言うならば、どうですか?
dart-bindgen Cヘッダーファイルにダーツffiバインディングを生成するためのツールですCLI とLibrary .
そして、我々はFFIを生成するためにそれを使用するには、ちょうどあなたの
ダーツとさびの他のハックのために調整されて、あなたは私に続くことができました、そしてgithub
ありがとう💚.
このブログ記事では、マルチスレッドAsync RustコードをAsync DARTで使用する方法と、1つ以上のランタイムを持つアプリケーションをどのように構築するかについて話しますsōzu 作品
問題🎯
Asyncのループを実行すると、常にそれを取得するのは簡単ではない、あなたはそれがDART VMが独自のAsyncのランタイムであり、錆では、我々は人気のランタイムのいずれかをプラグインすることができることを知っているTokio , async-std , 最近smol これは、低レベルのランタイムとして、async - stdを内蔵しています.
問題は、言語としてのDARTはシングルスレッドであるように設計されているということですが、それ自体は問題ではありませんが、後にどのようにRustを使うときに問題になるでしょう.
さて、あなたは以上を実行することができますIsolate これは、彼らが一緒に動作し、メッセージを渡すことによって通信する小さなダーツVMのインスタンスのようなものです、ここではフラッターチームからのアンドリューは、DARTの分離とイベントループについての話は、私は非常にダーツ分離についての考えがない場合は😅)
ウェブを掻きましょう🌍
このポストをよりエキサイティングにするために、単純なウェブをさびている図書館をこすって、フラッタアプリケーションでそれを使ってください.
両方の言語を接続する方法についての最後のブログの投稿からの経験を使用して、我々もここで同じ考えを使用します.
最初に錆木箱を作成します
$ cargo new --lib native/scrap # yes, it is a scrap lol
クールなので、我々は非同期のWebリクエストを送信する方法が必要ですので、使用してみましょうreqwest [dependencies]
# we will use `rustls-tls` here since openssl is an issue when cross-compiling for Android/iOS
reqwest = { version = "0.10", default-features = false, features = ["rustls-tls"] }
さあさびたコードを書きましょう😀// lib.rs
use std::{error, fmt, io};
/// A useless Error just for the Demo
#[derive(Copy, Clone, Debug)]
pub struct ScrapError;
impl fmt::Display for ScrapError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error While Scrapping this page.")
}
}
impl error::Error for ScrapError {}
impl From<reqwest::Error> for ScrapError {
fn from(_: reqwest::Error) -> Self {
Self
}
}
impl From<io::Error> for ScrapError {
fn from(_: io::Error) -> Self {
Self
}
}
/// Load a page and return its HTML body as a `String`
pub async fn load_page(url: &str) -> Result<String, ScrapError> {
Ok(reqwest::get(url).await?.text().await?)
}
あなたが見るように、sh * tyエラー処理による若干の単純なコードです😂次に、FFIバインディングを書き込みます
scrap
クレート$ cargo new --lib native/scrap-ffi
我々の中でCargo.toml
[lib]
name = "scrap_ffi"
crate-type = ["cdylib", "staticlib"]
[dependencies]
scrap = { path = "../scrap" }
今、我々はここでいくつかのことを考慮する必要があるので、それは汚くなるランタイムの設定🔌
我々は選択しますTokio 私たちのランタイムとしては、どのようなランタイムを使用するかは問題ではありません
reqwest
私たちがそれを使うつもりであるtokioでよく働きます.also lazy_static それは1秒で便利でしょう.
tokio = { version = "0.2", features = ["rt-threaded"] }
lazy_static = "1.4"
ランタイムを設定するコードを書きます// lib.rs
use tokio::runtime::{Builder, Runtime};
use lazy_static::lazy_static;
use std::io;
lazy_static! {
static ref RUNTIME: io::Result<Runtime> = Builder::new()
.threaded_scheduler()
.enable_all()
.core_threads(4)
.thread_name("flutterust")
.build();
}
/// Simple Macro to help getting the value of the runtime.
macro_rules! runtime {
() => {
match RUNTIME.as_ref() {
Ok(rt) => rt,
Err(_) => {
return 0;
}
}
};
}
この設定Tokio
4スレッドのランタイムと命名flutterust
各スレッドに対して.エラー処理🧰
ffiの間のエラーを処理するのは難しいので、我々は我々の人生を容易にするヘルパー枠を使用します.
追加ffi_helpers 依存する
ffi_helpers = "0.2"
これは、我々はエラーを処理するために役立つ可能性のある機能とマクロを提供し、公開last_error_length
and error_message_utf8
それで、DARTの側では、読みやすいエラーメッセージを得ることができました.macro_rules! error {
($result:expr) => {
error!($result, 0);
};
($result:expr, $error:expr) => {
match $result {
Ok(value) => value,
Err(e) => {
ffi_helpers::update_last_error(e);
return $error;
}
}
};
}
macro_rules! cstr {
($ptr:expr) => {
cstr!($ptr, 0);
};
($ptr:expr, $error:expr) => {{
null_pointer_check!($ptr);
error!(unsafe { CStr::from_ptr($ptr).to_str() }, $error)
}};
}
#[no_mangle]
pub unsafe extern "C" fn last_error_length() -> i32 {
ffi_helpers::error_handling::last_error_length()
}
#[no_mangle]
pub unsafe extern "C" fn error_message_utf8(buf: *mut raw::c_char, length: i32) -> i32 {
ffi_helpers::error_handling::error_message_utf8(buf, length)
}
ページの機能を公開する🔑
今、問題は、どのように我々は露出する
async
機能?よくC/Dartはどのように錆のアイデアはありませんasync
作品つの方法は、通常の非
async
のラッパとして機能するasync
関数と呼び出しblock_on 結果をasync fn
, 関数名がすべて表示されているのを見ると、そのスレッドをブロックしてasync
仕事、第一に利益は何ですか?もう一つの方法があります
callbacks
?!良い、それはオプションとJavaScriptを使用する
Promise
2つのコールバックを渡して、asyncタスクを処理するスタイルsuccess
そしてもう一つはerr
.理論的には動作しますが
前に言ったように、DARTはシングルスレッドであるように設計されているので、他のスレッドからコールバックを呼び出すことはできませんhere ).
では、解決策は何でしょうか?
私があなたに言ったならば、我々はDART
Isolate
😦.すべての新しい分離SendPort and ReceivePort これらは
Isolate
そして、あなただけが必要SendPort
メッセージを送信するIsolate
, また、あらゆるSendPort
何かを持っているNativePort このポートの基になる数.Just a small note, the term of
Port
here is not related at all to aNetworking
Port or something like that, it is just numbers used as Keys for a Map implemented internally in Dart VM, think of them as aHandle
to thatIsolate
我々がこれらの数を我々のものに使うならば、我々
async fn
私たちが望むスレッドからの結果は、可能ですか?はい分離する📞
allo-isolate 我々が築いた木箱ですSunshine DART VMと一緒にマルチスレッドの錆を実行するのを助けるために🌀
それは必要
Port
閉じるこの動画はお気に入りから削除されています.フードの下では、DART APIを使用します
Dart_PostCObject
関数とほとんど変換することができますall ?) ラストタイプにラストタイプのdocs を参照してください.Allo (pronounced Hello, without the H) usually used in communication over the phone in some languages like Arabic :).
私たちの例に戻ると、我々は今どのように公開する方法を知っている
load_page
関数使用allo-isolate
allo-isolate = "0.1"
使用するuse allo_isolate::Isolate;
...
#[no_mangle]
pub extern "C" fn load_page(port: i64, url: *const raw::c_char) -> i32 {
// get a ref to the runtime
let rt = runtime!();
let url = cstr!(url);
rt.spawn(async move {
// load the page and get the result back
let result = scrap::load_page(url).await;
// make a ref to an isolate using it's port
let isolate = Isolate::new(port);
// and sent it the `Rust's` result
// no need to convert anything :)
isolate.post(result);
});
1
}
偉大な、我々は行く準備が整いましたbinding.h
そして、すべての建物iOS
and Android
我々はまた、ダーツ側FFIを書く必要があります.UGH再び🤦♂️もし私があなたがCヘッダーファイルからあなたのダーツFCI結合を生成することができたと言うならば、どうですか?
DART BINDGEN:DART FFIを書く新しい方法
dart-bindgen Cヘッダーファイルにダーツffiバインディングを生成するためのツールですCLI とLibrary .
そして、我々はFFIを生成するためにそれを使用するには、ちょうどあなたの
build-dependencies
[build-dependencies]
cbindgen = "0.14.2" # Rust -> C header file
dart-bindgen = "0.1" # C header file -> Dart FFI
そして、我々のbuild.rs
use dart_bindgen::{config::*, Codegen};
fn main() {
...
let config = DynamicLibraryConfig {
ios: DynamicLibraryCreationMode::Executable.into(),
android: DynamicLibraryCreationMode::open("libscrap_ffi.so").into(),
..Default::default()
};
// load the c header file, with config and lib name
let codegen = Codegen::builder()
.with_src_header("binding.h")
.with_lib_name("libscrap")
.with_config(config)
.build()
.unwrap();
// generate the dart code and get the bindings back
let bindings = codegen.generate().unwrap();
// write the bindings to your dart package
// and start using it to write your own high level abstraction.
bindings
.write_to_file("../../packages/scrap_ffi/lib/ffi.dart")
.unwrap();
}
これまでのところ、我々はより高いレベルの抽象化を書きましょうffi.dart
ファイル.// packages/scrap_ffi/lib/scrap.dart
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
// isolate package help us creating isolate and getting the port back easily.
import 'package:isolate/ports.dart';
import 'ffi.dart' as native;
class Scrap {
// this only should be called once at the start up.
static setup() {
// give rust `allo-isolate` package a ref to the `NativeApi.postCObject` function.
native.store_dart_post_cobject(NativeApi.postCObject);
print("Scrap Setup Done");
}
Future<String> loadPage(String url) {
var urlPointer = Utf8.toUtf8(url);
final completer = Completer<String>();
// Create a SendPort that accepts only one message.
final sendPort = singleCompletePort(completer);
final res = native.load_page(
sendPort.nativePort,
urlPointer,
);
if (res != 1) {
_throwError();
}
return completer.future;
}
void _throwError() {
final length = native.last_error_length();
final Pointer<Utf8> message = allocate(count: length);
native.error_message_utf8(message, length);
final error = Utf8.fromUtf8(message);
print(error);
throw error;
}
}
フラッタアプリケーションでは、以下のようにパッケージを使用できます....
class _MyHomePageState extends State<MyHomePage> {
...
Scrap scrap;
@override
void initState() {
super.initState();
scrap = Scrap();
Scrap.setup();
}
// somewhere in your app
final html = await scrap.loadPage('https://www.rust-lang.org/');
それは、AndroidのエミュレータやIOSのシミュレータを起動し、実行する:$ cargo make # build all rust packages for iOS and Android
Then,$ flutter run # 🔥
すべてのコードはflutterust クローンとハックだけです😀.ダーツとさびの他のハックのために調整されて、あなたは私に続くことができました、そしてgithub
ありがとう💚.
Reference
この問題について(ダーツとさび:async物語🔃), 我々は、より多くの情報をここで見つけました https://dev.to/sunshine-chain/rust-and-dart-the-async-story-3adkテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol