当前位置: 首页 > news >正文

【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的知识的也是比较麻烦。

相关文章:

  • 【leetcode hot 100 84】柱状图中最大的矩形
  • LeetCode热题100题|1.两数之和,49.字母异位词分组
  • [WEB开发] Mybatis
  • CSP历年题解
  • Android 启动流程详解:从上电到桌面的全流程解析
  • Netty源码—7.ByteBuf原理四
  • K8s证书--运维之最佳选择(K8s Certificate - the best Choice for Operation and Maintenance)
  • 主键id设计
  • 华为OD机试A卷 - 积木最远距离(C++ Java JavaScript Python )
  • 文件描述符,它在哪里存的,exec()后还存在吗
  • 【STM32】对stm32F103VET6指南者原理图详解(超详细)
  • 支付页面安全与E-Skimming防护----浅谈PCI DSS v4.0.1要求6.4.3与11.6.1的实施
  • ✨分享我在飞书多维表格中使用DeepSeek的经历✨
  • STM32F103_LL库+寄存器学习笔记05 - GPIO输入模式,捕获上升沿进入中断回调
  • 飞速(FS)InfiniBand解决方案助力领先科技公司网络升级
  • kettle插件-mysql8数据库插件
  • MySQL进阶
  • 【linux复习】——进程间通信
  • 【HarmonyOS NEXT】EventHub和Emitter的使用场景与区别
  • 基于javaweb的SpringBoot雪具商城系统设计与实现(源码+文档+部署讲解)
  • 彭丽媛同巴西总统夫人罗桑热拉参观中国国家大剧院
  • 硅料收储挺价“小作文”发酵光伏板块罕见大涨,知情人士:确实在谈
  • 从“求生”到“生活”:医保纳入创新药让梗阻性肥厚型心肌病患者重拾生活掌控权
  • 国家统计局今年将在全国开展两次人口固定样本跟访调查
  • 农林生物安全全国重点实验室启动建设,聚焦重大有害生物防控等
  • 七方面118项任务,2025年知识产权强国建设推进计划印发