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

Rust 为什么不适合开发 GUI

前言

在当今科技蓬勃发展的时代,Rust 编程语言正崭露头角,逐步为世界上诸多重要基础设施提供动力支持。从存储海量信息到应用于 Linux 内核,Rust 展现出强大的实力。然而,当涉及构建 GUI(图形用户界面)时,Rust 却面临着诸多挑战。据数据显示,超过 56% 的 Rust 开发者认为其 GUI 开发亟待大幅改进,这也是许多人起初不愿采用 Rust 进行相关开发的重要原因。

Rust 的独特之处

Rust 自诞生之初,便以独特的姿态区别于其他编程语言。在众多编程语言中,垃圾回收机制较为常见,它能自动管理内存的分配与释放,极大减轻了开发者的负担。而 Rust 采用了所有权机制,这一机制在编译时生效。也就是说,值由变量拥有,变量可对值进行引用,当拥有变量超出作用域时,其所拥有的值会被自动释放。

此外,Rust 能够有效防范多线程同时访问相同数据的情况,即数据竞争问题。它通过确保同一时刻要么只有一个可变引用,要么有多个不可变引用,保证引用始终有效,并且当存在有效引用时,相关值不能被修改。同时,Rust 并非像 Java、C++ 或 JavaScript 那样的面向对象语言,它不支持抽象类和类继承。

例如,在面向对象语言中,通常会有一个顶层类 Component,其中包含 draw 方法,像按钮(Button)或文本(Text)等组件会继承自这个类并复用其函数。但在 Rust 中,情况有所不同,它使用 traits。开发者可以在库中添加一个名为 draw 的通用 trait,只要按钮对象、文本对象和图像对象实现了这个 Draw trait,它们就会被视为 UI 组件。甚至可以将一个随机的 sandwich 对象添加到 UI 组件库中,只要它实现了 Draw trait,当然这在实际开发中不太可能通过代码评审。

Rust 构建 GUI 之难

那么,究竟是什么让用 Rust 构建 GUI 如此困难呢?前面提到的 Rust 的独特之处,恰恰也是构建 GUI 时的阻碍因素。在编程领域,UI 通常被设计为树状结构,但使用 Rust 的继承机制构建树状结构极为困难。

以构建一个简单的登录界面为例,在 Android 开发中,视图树形结构有着清晰的层级关系。Android 的视图体系基于 View 和 ViewGroup 类。ViewGroup 是一个特殊的 View,它可以包含多个子 View,就像是树枝可以长出许多树叶一样,这就形成了一个树形结构。

比如,登录界面的最外层可能是一个 LinearLayout(线性布局,属于 ViewGroup 的一种),它决定了内部组件的排列方式是水平还是垂直。在这个 LinearLayout 里,可能有两个 EditText(输入框,属于 View)用于输入用户名和密码,还有一个 Button(按钮,同样属于 View)用于触发登录操作。

在代码实现上,开发者会在 XML 布局文件中描述这个树形结构。假设布局文件名为 activity_login.xml,代码可能如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/username_edit_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="用户名"/><EditTextandroid:id="@+id/password_edit_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="密码"android:inputType="textPassword"/><Buttonandroid:id="@+id/login_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="登录"/>
</LinearLayout>

在运行时,Android 系统会根据这个 XML 文件构建出相应的视图树形结构。当用户进行操作,比如点击按钮时,事件会从最上层的视图容器开始,沿着树形结构向下传递,找到对应的按钮 View 并触发相应的点击事件处理逻辑。

而在 Rust 中,由于缺乏像 Android 这种基于类继承的成熟视图体系,构建类似的树形结构就变得复杂许多。Rust 的 trait 不存储数据,导致每个组件需要自行管理其下的子组件,这使得遍历树状结构变得困难。

以构建登录界面为例,假设我们定义一个用于绘制 UI 组件的 trait,比如Draw:

trait Draw {fn draw(&self);
}

这里的 Draw trait 规定了实现它的类型必须拥有draw方法,但它并没有为实现它的类型提供存储数据的空间。当我们创建登录界面的各个组件(如输入框和按钮)并让它们实现 Draw trait 时,每个组件都需要自行处理数据存储的问题。

struct LoginButton {// 按钮的相关数据,如文本、位置等text: String,x: i32,y: i32,
}
impl Draw for LoginButton {fn draw(&self) {// 绘制按钮的逻辑,使用自身存储的数据println!("Drawing button with text: {}", self.text);}
}

在上述代码中,LoginButton 结构体实现了 Draw trait,它需要自己定义和管理数据(text、x、y)。相比之下,在 Android 中,视图类(如 EditText、Button)继承自 View 类,View 类及其父类会为子类提供一些默认的数据存储和管理机制,例如位置、大小等属性,子类可以直接使用或继承这些数据。

除了状态管理的不方便,Rust 的可变性规则也给 UI 组件状态的动态更新带来了挑战。Rust 的可变性规则主要用于确保内存安全和避免数据竞争。简单来说,在同一时间内,一个数据要么有多个不可变引用(可以理解为只读访问),要么只有一个可变引用(可以修改数据),但不能同时存在可变和不可变引用。

例如,在登录界面的场景中,如果我们要根据用户输入实时显示错误提示信息,在 Rust 中实现起来就不像在 Android 开发中那么直观。因为可能会涉及到状态的动态更新,此时遇到可变性规则的挑战。

假设我们有一个登录逻辑,需要根据用户名和密码的输入情况更新错误提示信息:

fn login(username: &str, password: &str) -> String {let mut error_message = String::new();if username.is_empty() {error_message.push_str("用户名不能为空");}if password.len() < 6 {if!error_message.is_empty() {error_message.push_str(", ");}error_message.push_str("密码长度至少为6位");}error_message
}

在这个例子中,error_message 是可变的,以便在不同的条件下添加错误信息。这个代码在单一线程运行,同一时刻只有一个可变引用指向 error_message,是可以通过编译的。但如果在更复杂的 UI 场景中,多个线程或不同的代码块同时尝试访问和修改 error_message,就会违反 Rust 的可变性规则,导致编译错误。因为 Rust 要保证数据在任何时刻的状态都是可预测的,避免出现数据竞争和未定义行为。

比如下面这样

use std::thread;fn main() {let mut error_message = String::new();let handle1 = thread::spawn(move || {error_message.push_str("线程 1 产生的错误");});let handle2 = thread::spawn(move || {error_message.push_str("线程 2 产生的错误");});handle1.join().unwrap();handle2.join().unwrap();println!("{}", error_message);
}

在这个示例里,多个线程同时尝试修改 error_message,Rust 编译器会检测到这种情况并报错,因为这违反了 Rust 的可变性规则,可能会引发数据竞争问题。

当然,多线程更新字符串的情况不多,也许有人说这个例子不具代表性,再看一个更具场景的情况。在 GUI 开发里,常常需要根据用户的操作动态更新 UI 状态,并且重新渲染视图。假设我们要开发一个简单的计数器界面,用户点击按钮时,计数器的值会增加。

在 Rust 里,为了保证内存安全,可变性规则会对状态更新和视图渲染之间的交互产生影响。以下是一个简化的示例代码:

// 假设这是一个简单的 UI 组件
struct Counter {value: u32,
}impl Counter {fn increment(&mut self) {self.value += 1;}fn draw(&self) {println!("当前计数器的值: {}", self.value);}
}fn main() {let mut counter = Counter { value: 0 };// 模拟用户点击按钮counter.increment();counter.draw();
}

在这个示例中,Counter 结构体表示一个计数器组件,increment 方法用于增加计数器的值,draw 方法用于渲染计数器的当前值。编译正常。

如果稍不注意, main 函数写成下面这样,编译就出错了

fn main() {let mut counter = Counter { value: 0 };let mut_ref = &mut counter;mut_ref.increment();// 这里会产生编译错误counter.draw(); 
}

上面 main 函数中,我们首先对 counter 进行了可变借用,创建了可变引用 mut_ref,并调用 mut_ref.increment() 方法对 counter 的值进行修改。

接着,我们尝试直接调用 counter.draw() 方法。但由于此时 counter 仍处于被可变借用的状态(mut_ref 的生命周期还未结束),Rust 的可变性规则不允许在可变借用期间对同一个数据进行不可变借用。因此,counter.draw() 这行代码会导致编译错误。

error[E0502]: cannot borrow `counter` as immutable because it is also borrowed as mutable--> src/main.rs:17:5|
15 |     let mut_ref = &mut counter;|                   -------- mutable borrow occurs here
16 |     mut_ref.increment();
17 |     counter.draw(); |     ^^^^^^^ immutable borrow occurs here
18 | }| - mutable borrow ends here

通过正确和错误两个版本代码的对比,可以看出

  • 错误版本:对 counter 进行了可变借用,创建了可变引用 mut_ref,并且在可变借用的生命周期内尝试对 counter 进行不可变借用,违反了 Rust 的可变性规则。
  • 正确版本:没有同时存在可变借用和不可变借用的冲突情况。先直接调用 counter.increment() 方法对 counter 进行可变操作,操作完成后,可变借用结束,再调用 counter.draw() 方法进行不可变操作,符合 Rust 的可变性规则。

但在实际的 GUI 应用中,UI 组件的状态可能会受到多个因素的影响,状态更新和视图渲染的逻辑也会更加复杂,很可能需要根据不同的条件更新多个 UI 组件的状态,并且在合适的时机进行视图渲染。Rust 的可变性规则会让这种状态管理变得更加困难,稍有不慎就会出现编译错误,增加了开发者的心智负担。

应对之策与实践探索

尽管困难重重,但并非毫无解决办法。有一个专门的网站 https://areweguiyet.com/ 致力于更新 Rust 在 GUI 开发方面的进展情况。在开源社区,也有许多项目取得了显著进展,比如 ICED 或 Tauri,它们使用 Rust 为原生 Web 视图提供支持。

另一种有效的解决方案是完全摒弃面向对象编程,深入采用 Rust 的方式来处理问题。例如使用 ELM 架构,它由模型(Model)、视图(View)和更新(Update)组成。模型存储视图的所有状态,视图将模型数据转换为屏幕上可见的内容,更新则负责使用程序员定义的对象 “MSGs” 来修改模型。

在这里插入图片描述

这种架构其实就是 Android 近年来推崇的 UDF(单项数据流),在 Rust 中实现这种架构有诸多优势,它是功能性且可变的,开发者无需直接修改数据,因为数据始终通过更新函数进行处理。例如,可以插入一个全新的值,由于模型只有一个单一所有者,不会触发任何警报。此外,Rust 的枚举(Enums)使得确定不同数据类型变得容易,开发者可以在代码中轻松进行模式匹配,ICED 项目就有很好的示例展示如何使用 Rust 枚举通过按钮来增加或减少数字。
但是 ELM 架构并非完美无缺,也有一些尝试对其进行替代的方案,其中一种替代方案是实体组件系统架构(Entity Component System Architecture)。在这种架构中,Entity(实体)和 Component(组件)是两个核心概念。

Entity 可以理解为一个唯一的标识符,它本身不包含任何数据或行为。在 ECS 架构里,Entity 就像是一个容器或者一个 “占位符”,用于将不同的 Component 组合在一起。例如一个游戏中,它代表其中一个角色、一个道具,或者 GUI 界面中的一个按钮、一个文本框等。在 Rust 中,Entity 通常用一个简单的整数 ID 来表示。

Component 是包含数据的最小单元,它只负责存储特定类型的数据,而不包含任何行为。例如,在一个游戏中,可能有表示位置的 PositionComponent、表示速度的 VelocityComponent;在 GUI 开发中,可能有表示文本内容的 TextComponent、表示颜色的 ColorComponent 等。每个 Component 专注于一种特定的属性或状态。

在这里插入图片描述

著名的 Warp 终端项目就采用了这种方式实现。将每个组件称为 view,并赋予其一个唯一的 ID,即 entity id。每个窗口存储实体 ID 到实际视图的映射,通过这种方式存储与视图相关的任何状态,并且可以存储每个视图到父视图的映射,以便在树状结构中向上遍历。这些数据以一系列由系统拥有的映射和列表形式存储,这是目前在 Rust 中模拟面向对象编程语言最接近的方式。

通过这种实现方式,Warp 能够创建丰富的 UI 元素,并且性能几乎可与其他任何终端媲美。如果读者对此感兴趣,可以通过视频描述中的链接免费下载 Warp 来体验其 GUI。

展望

在 Rust 构建 GUI 的领域中,尽管充满挑战,但通过不断探索和创新,开发者们已经找到多种有效的解决途径,并且在实践中取得了不错的成果,未来 Rust 在 GUI 开发方面有望迎来更广阔的发展前景 。

相关文章:

Rust 为什么不适合开发 GUI

前言 在当今科技蓬勃发展的时代&#xff0c;Rust 编程语言正崭露头角&#xff0c;逐步为世界上诸多重要基础设施提供动力支持。从存储海量信息到应用于 Linux 内核&#xff0c;Rust 展现出强大的实力。然而&#xff0c;当涉及构建 GUI&#xff08;图形用户界面&#xff09;时&…...

DisplayPort版本对比

目前视频接口标准基本上被HDMI和DisplayPort两大标准给瓜分天下了。 &#xff08;1&#xff09; HDMI由消费电子厂商主导&#xff0c;最新版本&#xff08;如HDMI 2.1&#xff09;理论带宽为48Gbps&#xff0c;主要应用于电视、游戏主机及家庭影音设备。 &#xff08;2&#xf…...

YOLOv12即插即用-Pconv(风车卷积)

1.模块介绍 PinwheelConv(风车状卷积)充分利用了IRST(红外搜索与跟踪)中的高斯分布特性,以极少的参数实现了高效且更大感受野的特性。此外,本文还提出了一种简单而高效的 SD 损失函数,有效缓解了标签 IoU 变化带来的不稳定性。通过与现有卷积模块和损失函数的广泛对比,…...

AI知识补全(十四):零样本学习与少样本学习是什么?

名人说&#xff1a;一笑出门去&#xff0c;千里落花风。——辛弃疾《水调歌头我饮不须劝》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;AI知识补全&#xff08;十三&#xff09;&#xff1a;注意力…...

Pycharm(十一):字符串练习题

1.输入一个字符串&#xff0c;打印所有偶数位上的字符(下标是0&#xff0c;2&#xff0c;4&#xff0c;6...位上的字符) # 练习题1:输入一个字符串&#xff0c;打印所有偶数位上的字符(下标是0&#xff0c;2&#xff0c;4&#xff0c;6...位上的字符) # 1.键盘录入字符串&…...

设计原则之迪米特法则

一、定义 迪米特法则又称为最少知识原则&#xff08;Law of Demeter&#xff0c;LoD&#xff09;&#xff0c;是一项用于面向对象设计的基本原则之一。该原则强调一个对象应该对其他对象有最少的了解&#xff0c;即一个类不应该知道太多关于其他类的内部细节。 二、好处 迪米…...

Debian系统_主板四个网口1个配置为WAN,3个配置为LAN

Debian系统_主板四个网口1个配置为WAN&#xff0c;3个配置为LAN 一、重新配置网口 1、查看当前网口的状态 ifconfig 或者 ip link show 或者 ls /sys/class/net 2、修改网络配置文件 sudo vi /etc/network/interfaces 注意WAN口的网关地址如果是192.168.3.1的话&#xff0c;L…...

Spring Boot 快速入手

前言&#xff1a;为什么选择 Spring Boot&#xff1f; &#x1f680; 在现代 Java 开发中&#xff0c;Spring Boot 已成为最流行的后端框架之一。无论是小型 Web 应用、企业级系统&#xff0c;还是微服务架构&#xff0c;Spring Boot 都能提供快速开发、自动配置、轻量级部署的…...

Ubuntu Live USB 如何使用

以下是使用 Ubuntu Live USB 的详细指南&#xff0c;涵盖启动、试用系统、安装系统及常用工具操作&#xff1a; 1. 制作 Ubuntu Live USB • 所需工具&#xff1a; • Ubuntu ISO 镜像&#xff08;从 官网 下载&#xff09;。 • U盘&#xff08;至少 4GB&#xff0c;数据将被…...

基于单片机的音乐播放器系统设计

基于单片机的音乐播放器系统设计是一个综合性较强的电子系统开发项目 系统概述 基于单片机的音乐播放器旨在利用单片机的控制功能,结合音频处理电路、存储单元等,实现音乐的播放、暂停、切换、音量调节等功能,可应用于小型便携式音频设备、电子玩具、智能家居背景音乐系统等…...

HttpClient-01.介绍

一.介绍 通过HttpClient&#xff0c;我们可以在Java程序中构造并发送Http请求。要使用HttpClient&#xff0c;就要导入依赖坐标。 核心API&#xff1a; HttpClient&#xff1a;Http客户端&#xff0c;使用它可以发送http请求。 HttpClients&#xff1a;构建器&#xff0c;使…...

2025年win10使用dockerdesktop安装k8s

一、写作背景 百度了一圈&#xff0c; 要么教程老&#xff0c;很多操作步骤冗余&#xff0c; 要么跑不通&#xff0c;或者提供的链接失效等情况。 二、看前须知 1、安装过程使用的AI辅助&#xff0c; 因为参考的部分博客卡柱了。 2、如果操作过程中遇到卡顿&#xff0c; …...

技术回顾day2

1.获取文件列表 流程&#xff1a;前端根据查询条件封装查询信息&#xff0c;后端接收后进行封装&#xff0c;封装为FileInfoQuery,根据fileInfoQuery使用mybatis的动态sql来进行查询。 2.文件分片上传 每次上传需要上传包括(文件名字&#xff0c;文件&#xff0c;md5值&#…...

软件工程-UML

例图&#xff0c;类图&#xff0c;状态图&#xff0c;顺序图&#xff0c;活动图 目录 例图 类图 状态图 顺序图 活动图 例图 例图由四个元素组成&#xff0c;参与者、用例、系统边界、参与者和用例之间的关系 参与者用一个小人表示&#xff0c;用例用椭圆表示&#xff…...

赛逸展2025“创新引擎”启动:限量席位,点亮科技绿色新征程

当今时代&#xff0c;科技革新与绿色发展已然成为推动社会进步的双引擎。2025第七届亚洲消费电子技术贸易展&#xff08;赛逸展&#xff09;敏锐捕捉这一趋势&#xff0c;重磅打造“科技创新专区”&#xff0c;并面向科技、绿色企业吹响限量招募号角。 这个独具特色的专区紧扣…...

前后台系统

前后台系统&#xff08;Foreground/Background System&#xff09;是一种常见的嵌入式系统编程模型&#xff0c;尤其是在那些不需要复杂操作系统的简单系统中。这种系统架构通常用于实时性要求不是极端苛刻的环境&#xff0c;但在响应外部事件时仍需要一定的及时性。下面我将详…...

VHT AMPDU

A - MPDU 由一个或多个 A - MPDU 子帧以及可变数量的 EOF填充子帧组成。 在 VHT中,存在如下填充: 一个 A - MPDU 子帧的填充子字段中有 0 - 3 个字节。 EOF 填充字段中有零个或多个 EOF 填充子帧。 EOF 填充子帧中EOF填充字节中有 0 - 3 个字节。 A - MPDU 帧结束前填充(A -…...

Spring框架如何做EhCache缓存?

在Spring框架中&#xff0c;缓存是一种常见的优化手段&#xff0c;用于减少对数据库或其他资源的访问次数&#xff0c;从而提高应用性能。Spring提供了强大的缓存抽象&#xff0c;支持多种缓存实现&#xff08;如EhCache、Redis、Caffeine等&#xff09;&#xff0c;并可以通过…...

助力 Windows 文件管理:重命名与清理重复文件软件精选

软件介绍 在日常的电脑使用中&#xff0c;高效管理文件至关重要。接下来为大家介绍几款实用软件&#xff0c;能助您轻松搞定文件管理难题。 AdvancedRenamer&#xff1a;全能批量重命名利器 专为 Windows 系统打造的 AdvancedRenamer&#xff0c;功能堪称强大。无论是文本替…...

重建二叉树(C++)

目录 1 问题描述 1.1 示例1 1.2 示例2 1.3 示例3 2 解题思路 3 代码实现 4 代码解析 4.1 初始化 4.2 递归部分 4.3 主逻辑 5 总结 1 问题描述 给定节点数为 n 的二叉树的前序遍历和中序遍历结果&#xff0c;请重建出该二叉树并返回它的头结点。 例如输入前序遍历序…...

Go+Gin实现安全多文件上传:带MD5校验的完整解决方案

后端 package mainimport ("encoding/json""fmt""log""net/http""os""path/filepath""github.com/gin-contrib/cors""github.com/gin-gonic/gin" )// 前端传来的文件元数据 type FileMetaRe…...

SwanLab Slack通知插件:让AI训练状态同步更及时

在AI模型训练的过程中&#xff0c;开发者常常面临一个难题&#xff1a;如何及时跟踪训练状态&#xff1f;无论是实验超参数的调整、关键指标的变化&#xff0c;还是意外中断的告警&#xff0c;传统的监控方式往往依赖手动刷新日志或反复检查终端&#xff0c;这不仅效率低下&…...

JavaScript元素尺寸与位置

目录 client 家族与 offset 家族 一、client 家族&#xff1a;内容区域 内边距 示例代码 应用场景 二、offset 家族&#xff1a;内容区域 内边距 边框 滚动条 示例代码 应用场景 三、综合应用场景 1. 动态调整元素高度 2. 拖拽元素 3. 判断元素是否在视口内 四…...

IS-IS:单区域集成配置与多区域集成配置

一、IS-IS概述 IS-IS&#xff08;Intermediate System to Intermediate System&#xff09; 是一种链路状态内部网关协议&#xff08;IGP&#xff09;&#xff0c;设计用于自治系统&#xff08;AS&#xff09;内部的路由选择。最初由ISO为OSI模型的无连接网络服务&#xff08;…...

Cesium学习(未完继续)

添加底图 viewer.imageryLayers.addImageryProvider(imageryProvider)常见 ImageryProvider 实现类 ArcGisMapServerImageryProvider&#xff1a;用于从 ArcGIS Server 获取影像数据。 BingMapsImageryProvider&#xff1a;用于从 Bing Maps 获取影像数据。 OpenStreetMapIm…...

【学Rust写CAD】22 双圆径向渐变的结构体(two_circle_radial_gradient.rs)

源码 //two_circle_radial_gradient.rs //! 定义双圆径向渐变的结构体和相关功能/// 表示一个双圆径向渐变的源 /// /// 该结构体描述了两个圆之间的渐变&#xff0c;支持矩阵变换和颜色查找表优化 #[derive(Debug, Clone, PartialEq)] pub struct TwoCircleRadialGradientSou…...

基于SpringAOP面向切面编程的一些实践(日志记录、权限控制、统一异常处理)

前言 Spring框架中的AOP&#xff08;面向切面编程&#xff09; 通过上面的文章我们了解到了AOP面向切面编程的思想&#xff0c;接下来通过一些实践&#xff0c;去更加深入的了解我们所学到的知识。 简单回顾一下AOP的常见应用场景 日志记录&#xff1a;记录方法入参、返回值、执…...

acwing 5438. 密接牛追踪2

农夫约翰有 NN 头奶牛排成一排&#xff0c;从左到右依次编号为 1∼N。 不幸的是&#xff0c;有一种传染病正在蔓延。 最开始时&#xff0c;只有一部分奶牛受到感染。 每经过一个晚上&#xff0c;受感染的牛就会将病毒传染给它左右两侧的牛&#xff08;如果有的话&#xff09…...

【Linux】线程池

目录 线程池 日志 线程池 在程序中&#xff0c;会预先创建一批线程&#xff0c;在内部会有一个叫任务队列task_queue的东西&#xff0c;未来如果有任务要处理&#xff0c;就把任务push到任务队列里&#xff0c;然后自动有线程去任务队列里拿任务并处理&#xff0c;如果没有任…...

【11408】考研英语长难句解析策略:三步断开与简化法,快速提升阅读得分

2025.04.01 英语断开长难句分析主谓心得 简化长难句心得 英语 断开长难句 在一些长难句中&#xff0c;有时从句的连词会被省略&#xff0c;且没有标点将其隔开&#xff0c;此时就无法通过标点和连接词来断开长难句。那么我们只能够通过分析主谓来断开长难句。 分析主谓 谓语…...

Spring Cloud 2023.x安全升级:OAuth2.1与JWT动态轮换实战

引言&#xff1a;当安全遇上云原生&#xff0c;零停机密钥轮换成为刚需 在微服务架构中&#xff0c;OAuth2.1与JWT已成为身份验证的黄金标准&#xff0c;但传统方案存在两大痛点&#xff1a; 密钥轮换风险&#xff1a;手动替换JWT密钥需重启服务&#xff0c;导致短暂鉴权中断&…...

Vue3.5 企业级管理系统实战(十二):组件尺寸及多语言实现

1 组件尺寸切换 1.1 用 Pinia 进行 Size 的持久化存储 首先&#xff0c;在 src/plugins/element.ts 中增加 size 类型&#xff0c;代码如下&#xff1a; //src/plugins/element.ts import type { App } from "vue";import { ElMessage, ElNotification, ElMessageBo…...

15:00开始面试,15:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…...

Java学习路线 - 第三阶段笔记

Java学习路线 - 第三阶段笔记 Java高级特性&#xff08;2-3个月&#xff09; 1. 集合框架深入 1.1 List详解 ArrayList&#xff1a;基于动态数组实现&#xff0c;随机访问高效&#xff0c;插入删除效率低LinkedList&#xff1a;基于双向链表实现&#xff0c;插入删除高效&a…...

【无标题】Scala函数基础

函数和方法的区别 1&#xff09; 核心概念 &#xff08;1&#xff09; 为完成某一功能的程序语句的集合&#xff0c;称为函数。 &#xff08;2&#xff09; 类中的函数称之方法。 2&#xff09; 案例实操 &#xff08;1&#xff09; Scala 语言可以在任何的语法结构中声明…...

微信登录、商品浏览前瞻

一.业务效果 二.所需技术...

自动化工作流工具的综合对比与推荐

最近收到很多朋友私信我说&#xff1a;“刷短视频的时候&#xff0c;总是刷到自动化工作流的工具&#xff0c;有好多直播间都在宣传&#xff0c;不知道哪款工具好”。我花了点时间&#xff0c;做了一下测试&#xff0c;大家可以参考一下&#xff0c;以下内容&#xff1a; 以下…...

可实现黑屏与蓝屏反应的屏幕隐私保护软件分享

软件介绍 在信息安全备受关注的当下&#xff0c;一款能够有效保护屏幕隐私的软件 —— 防窥助手&#xff0c;悄然问世。它由吾爱的 遗憾迟香精心开发&#xff0c;为用户的屏幕隐私防护带来全新体验。 独特原理&#xff0c;精准守护 防窥助手的运行原理相当巧妙&#xff0c;它…...

PERL开发环境搭建>>Windows,Linux,Mac OS

特点 简单 快速 perl解释器直接对源代码程序解释执行,是一个解释性的语言, 不需要编译器和链接器来运行代码>>速度快 灵活 借鉴了C/C, Basic, Pascal, awk, sed等多种语言, 定位于实用性语言,既具备了脚本语言的所有功能,也添加了高级语言功能 开源.免费 没有&qu…...

【实战】渗透测试下的传输命令

目录 bitsadmin certutil curl ftp js nc perl php py scp vbs wget WindowsDefender bitsadmin 不支持https、ftp协议&#xff0c;php python带的服务器会出错 >bitsadmin /transfer n http://192.168.1.192/Client.exe e:\1.exe >bitsadmin /rawreturn /…...

JSON 基础知识(一)

第一部分&#xff1a;JSON 基础知识 &#x1f4e2; 快速掌握 JSON&#xff01;文章 视频双管齐下 &#x1f680; 如果你觉得阅读文章太慢&#xff0c;或者更喜欢 边看边学 的方式&#xff0c;不妨直接观看我录制的 JSON 课程视频&#xff01;&#x1f3ac; 视频里会用更直观…...

nodejs:midi-writer-js 将基金净值数据转换为 midi 文件

开放式基金是没有公布每日交易量的。 /funds/data/660008.csv 文件开头&#xff1a; date,jz,ljjz 2016-01-04,1.1141,1.1141 2016-01-05,1.1161,1.1161 2016-01-06,1.1350,1.1350 这是一个将开放式基金数据转换为 MIDI音乐的 js 程序示例。该程序将基金净值映射为 MIDI音符的…...

从零实现Json-Rpc框架】- 项目实现 - 服务端registrydiscovery实现

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

自适应二值化与形态学变换在图像颜色识别与替换中的应用解析

目录 前言一、自适应二值化1.1 取均值 ADAPTIVE_THRESH_MEAN_C1.2 高斯加权求和 ADAPTIVE_THRESH_GAUSSIAN_C1.2.1 一维高斯分布1.2.2 二维高速分布1.2.3 二维高斯分布权重计算规则 1.2.3.1 用户设置了σ1.2.3.2 用户没有设置σ1.3 代码二、形态学变换2.1 核 2.2 腐蚀2.3 膨胀…...

JsonCpp 处理 JSON(现代 C++ 方案)(三)

第三部分:JsonCpp 处理 JSON(现代 C++ 方案) 📢 快速掌握 JSON!文章 + 视频双管齐下 🚀 如果你觉得阅读文章太慢,或者更喜欢 边看边学 的方式,不妨直接观看我录制的 JsonCpp 课程视频!🎬 视频里会用更直观的方式讲解 JsonCpp 的核心概念、实战技巧,并配有动手演…...

flutter 曲线学习 使用第三方插件实现左右滑动

flutter 曲线的使用 实现左右滑动 TemperatureChartPage() TemperatureChartPage2() – 不太完善 方法 ChartDrawPage import package:doluyo/dly_package/widget/dly_widget.dart; import package:fl_chart/fl_chart.dart; import package:flutter/material.dart; impor…...

【WRF工具】GIS4WRF详细介绍:配置 WPS/WRF

【WRF工具】GIS4WRF详细介绍 QGIS-GIS4WRF安装&#xff08;Installation&#xff09;安装 QGIS安装 GIS4WRF GIS4WRF 配置(Configuration)一、如何进入配置界面二、可配置内容1️⃣ 设置工作目录2️⃣ 与 WPS/WRF 集成3️⃣ 与 NCAR 数据档案集成 参考 GIS4WRF 是一个在 QGIS 中…...

【自用记录】本地关联GitHub以及遇到的问题

最近终于又想起GitHub&#xff0c;想上传代码和项目到仓库里。 由于很早之前有在本地连接过GitHub&#xff08;但没怎么用&#xff09;&#xff0c;现在需要重新搞起&#xff08;操作忘得差不多&#xff09;。 在看教程实操的过程中遇到了一些小问题&#xff0c;遂记录一下。 前…...

小程序中跨页面组件共享数据的实现方法与对比

小程序中跨页面/组件共享数据的实现方法与对比 在小程序开发中&#xff0c;实现不同页面或组件之间的数据共享是常见需求。以下是几种主要实现方式的详细总结与对比分析&#xff1a; 一、常用数据共享方法 全局变量&#xff08;getApp()&#xff09;、本地缓存&#xff08;w…...

ngx_http_core_merge_srv_conf

定义在 src\http\ngx_http_core_module.c static char * ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_core_srv_conf_t *prev parent;ngx_http_core_srv_conf_t *conf child;ngx_str_t name;ngx_http_server_name_t…...