【Tauri2】004——run函数的简单介绍(2)
前言
前面介绍了run函数的第一行代码
后面接着介绍。
说明
在后面的文字中表述“点击”,意思是按住ctrl+鼠标左击
写的时候,既有中文,也有英文,感觉没办法,有些时候用中文不知道怎么表达。
简单介绍函数签名之类的,内部很复杂。
正文
第二行
这很简单
pub fn run()
pub:声明为公开的,类似于public
fn:声明函数的关键字
run 函数名
返回值为 ()
第三行
tauri::Builder::default()
Builder
按住ctrl,左击Builder
#[allow(clippy::type_complexity)]
pub struct Builder<R: Runtime>
#[allow(clippy::type_complexity)]是一个属性,不重要。
可以看出Builder是个结构体,R是泛型,Runtime是特性(trait)
trait类似于其他语言的接口,但是功能更强大。
R:Runtime 是个trait bounds(trait 约束)
Trait and lifetime bounds - The Rust Referencehttps://doc.rust-lang.org/reference/trait-bounds.html
意思是R需要实现Runtime这个trait
如果点进Runtime,还可以发现Runtime内部也有trait bound,这就会搞得很复杂。笔者就不展示了。
最关键一点,泛型R需要实现Runtime这个trait。
Builder是一个结构体,可以查看其中有许多字段,其中有几个比较重要的
invoke_handler :JS消息处理器
setup:启动时进行一些操作
plugins:插件管理
state:全局状态管理
menu:菜单
menu_event_listeners:菜单事件监听器
window_event_listeners:窗口事件监听器
以后慢慢说
接着左击default
对default来说
#[cfg(feature = "wry")]
#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
impl Default for Builder<crate::Wry> {
fn default() -> Self {
Self::new()
}
}
两个# ,一个是条件编译,一个是条件属性,具体含义不必细说。
关注 impl Default for Builder<crate::Wry>
总体上,这是一个为结构体实现trait的语法,表达形式如下
impl trait for struct{
// 需要实现trait中的方法,除了默认方法
}
简单地使用这个语法
新建一个test2项目,结构如下
在book.rs中
// 声明trait
pub trait Book{
// 需要实现的方法
fn read(&self);
// 默认实现的方法
fn write(&self){
println!("write");
}
}
// 声明结构体
pub struct User {
pub name: String,
pub age: u32,
}
// 为结构体user实现trait
impl Book for User{
fn read(&self) {
println!("{}在{}岁读书",self.name,self.age);
}
}
在main函数中
mod book;
use book::{User, Book};
fn main() {
let user = User {
name: String::from("Alice"),
age: 30,
};
user.read();
user.write();
}
结果如下
言归正传
Builder是带有泛型的。因此,这里有两个问题
1、Wry是否满足实现了Runtime
2、是否实现了Default中的方法
查看是否实现了Runtime
点击Wry
pub type Wry = tauri_runtime_wry::Wry<EventLoopMessage>;
发现type这个关键字在,说明Wry是个别名
再点击右边的Wry,发现
pub struct Wry<T: UserEvent> {
context: Context<T>,
event_loop: EventLoop<Message<T>>,
}
这是一个结构体,因此需要为这个结构实现Runtime,往下翻的过程中
发现了是实现Runtime。
是否实现了Default中的方法
在Default中,可以发现只有一个函数签名
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "default_fn"]
fn default() -> Self;
因此,需要实现方法default,在括号里面就有这个方法。
fn default() -> Self {
Self::new()
}
在default中可以看出,调用new,返回了实例Builder<Wry>。
第四行
.plugin(tauri_plugin_opener::init())
点击一下plugin,跳转如下
#[must_use]
pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self {
self.plugins.register(Box::new(plugin));
self
}
#[must_use] 是个属性,作用是如果调用了这个属性修饰的方法,但是没有使用返回值,就会发出警告。
看参数,第一个参数是mut self ,获得实例的所有权并且可变。
第二个参数是plugin,类型是个泛型P
P的约束 是Plugin<R> + 'static,
Plugin<R>是trait bounds 。点击Plugin
发现R也需要实现Runtime
'static 是生命周期约束,表示在整个程序运行一直存活
很容易理解,注册的插件肯定要一直能使用,不然程序运行到某个关键节点,插件没了,不就会出现巨大的bug。
对于plugin方法内部具体实现,不细说
返回Self,意思就是返回Builder<Wry>实例
对于plugin方法所在的位置,必然是结构体Builder通过impl实现的
简单看看tauri_plugin_opener的init方法
返回了TauriPlgin,点击,确实是实现Plugin
没有问题。
第五行
.invoke_handler(tauri::generate_handler![greet])
greet函数
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
首先
看到tauri::generate_handler! ,准确地说,看到 ! ,可以判断这个一个宏,
Procedural Macros - The Rust Referencehttps://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros点击generate_handler
#[proc_macro]
pub fn generate_handler(item: TokenStream) -> TokenStream {
parse_macro_input!(item as command::Handler).into()
}
看到proc_macro,这是一个函数宏(Function-like macros)
如果看到proc_macro_derive,那就是派生宏
如果看到proc_macro_attribute,那就是属性宏,比如前面说的mobile_entry_point
这些都是过程宏,还有声明宏
比如,打印语句println!,这是一个声明宏(Macros By Example )
macro_rules! println
Macros By Example - The Rust Referencehttps://rustwiki.org/en/reference/macros-by-example.html
简单使用一下函数宏
新建一个library cate中,需要依赖syn和quote,以及需要在设置proc-macro = true
src/lib.rs
use proc_macro::TokenStream;
use syn::{parse_macro_input, Ident, LitStr};
use quote::quote;
#[proc_macro]
pub fn get_function(item: TokenStream) -> TokenStream {
println!("开始解析");
// item 是传入的参数,这里是一个&str
let fn_string=parse_macro_input!(item as LitStr);
// 获取字符串的值,并转换为Ident类型
let fn_name=Ident::new(&fn_string.value(),fn_string.span());
// 生成新的代码
let result=quote!{
// 生成函数
fn #fn_name(){
println!("Hello, world!");
}
};
result.into()
}
传入一个字符串,可以变成函数,运行函数,打印hello world
main.rs中
use macros::get_function;
get_function!("run");
fn main() {
run();
}
运行结果如下
言归正转
继续往下走,invoke_handler函数签名如下
#[must_use]
pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
where
F: Fn(Invoke<R>) -> bool + Send + Sync + 'static,
重点关注F
where
关键字的主要作用是将约束从类型参数或返回值的定义中分离出来,对于一个泛型,如果约束太多,就适合使用where。
Fn(Invoke<R>) -> bool :是个闭包或者函数,返回值是bool
Send+Sync是tauri bounds
Send
:可以安全跨线程传递。
Sync
:可以多线程共享引用
还有一个不必细说。
invoke_handler的返回值是Self。
第5行的实现可以说是最重要的一步,这一步注册了带有Command的函数,而且函数在整个生命周期一直存活,使用函数完成进程通信。
第六行
.run(tauri::generate_context!())
这其实就很简单了,
首先,run,启动的意思。
generate_context很明显是个函数宏,和上一行一样,代码如下
#[proc_macro]
pub fn generate_context(items: TokenStream) -> TokenStream {
// this macro is exported from the context module
let path = parse_macro_input!(items as ContextItems);
context::generate_context(path).into()
}
点击ContextItems
发现有config_file、capabiliies等之类的
笔者明白了,这是在进行注册配置
往下翻,可以看到tauri.conf.json。
看来第六行是在进行配置。
点击一下run
pub fn run(self, context: Context<R>) -> crate::Result<()> {
self.build(context)?.run(|_, _| {});
Ok(())
}
需要Context对象,而且进行了build,build完成之后,进行run,最后返回Result。
第七行
.expect("error while running tauri application");
处理错误,自定义报错的消息。
总结
整个run函数简单的看了看,可以说是Tauri后台的核心。
其中主要的结构体Builder,重中之重,很多东西都是围绕它来展开。其中有许多方法,以后慢慢介绍。
整个run函数涉及到的Rust的知识的也是比较麻烦。