Rust 数据结构:String
Rust 数据结构:String
- Rust 数据结构:String
- 什么是字符串?
- 创建新字符串
- 更新字符串
- 将 push_str 和 push 附加到 String 对象后
- 使用 + 运算符和 format! 宏
- 索引到字符串
- 字符串在内存中的表示
- 字节、标量值和字形簇
- 分割字符串
- 遍历字符串的方法
Rust 数据结构:String
在本文中,我们将讨论每种集合类型都具有的 String 操作,例如创建、更新和读取。我们还将讨论 String 与其他集合的不同之处,即由于人和计算机解释 String 数据的方式不同,对 String 进行索引变得复杂。
什么是字符串?
Rust 在核心语言中只有一种字符串类型,它是字符串切片 str,通常以它的借用形式 &str 出现。字符串切片是对存储在其他地方的一些 UTF-8 编码字符串数据的引用。例如,字符串字面值存储在程序的二进制文件中,因此是字符串切片。
String 类型是由 Rust 的标准库提供的,而不是编码到核心语言中,它是一个可增长的、可变的、拥有的、UTF-8 编码的字符串类型。
当在 Rust 使用“字符串”时,它们可能指的是 String 或 String slice &str 类型,而不仅仅是其中一种类型。虽然本文主要是关于 String 的,但这两种类型在 Rust 的标准库中都大量使用,并且 String 和 String 切片都是 UTF-8 编码的。
创建新字符串
String 实际上是作为字节向量的包装器实现的,带有一些额外的保证、限制和功能,所以在使用上很多和 vector 类似。
let mut s = String::new();
这一行创建了一个新的空字符串 s,然后我们可以将数据加载到其中。
通常,我们会有一些初始数据,我们想用这些数据开始字符串。为此,我们使用 to_string 方法,该方法可用于任何实现 Display trait 的类型,就像字符串字面量一样。
let data = "initial contents";let s = data.to_string();// The method also works on a literal directly:let s = "initial contents".to_string();
还可以使用 String::from 函数从字符串字面值创建 String。
let s = String::from("initial contents");
更新字符串
String 的大小可以增长,其内容可以改变。
将 push_str 和 push 附加到 String 对象后
push_str 方法接受一个字符串切片,并且不获取参数的所有权。
let mut s = String::from("foo");s.push_str("bar");
push 方法接受单个字符作为参数,并将其添加到 String 中。
let mut s = String::from("lo");s.push('l');
使用 + 运算符和 format! 宏
+ 操作符可以组合两个现有字符串。
let s1 = String::from("Hello, ");let s2 = String::from("world!");let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
s1 在相加之后不再有效的原因,以及我们使用 s2 引用的原因,与使用 + 操作符时调用的方法的签名有关。+ 操作符使用 add 方法,其签名看起来像这样:
fn add(self, s: &str) -> String {
首先,s2 有一个 &,这意味着我们将第二个字符串的引用添加到第一个字符串。
我们能够在 add 调用中使用&s2(String 类型)的原因是编译器可以将 &String 实参强制转换为 &str。当我们调用 add 方法时,Rust 使用了一个强制转换,它将 &s2 转换为 &s2[…]。因为 add 没有获得 s 形参的所有权,所以在这个操作之后 s2 仍然是一个有效的 String。
其次,我们可以在签名中看到 add 取得了 self 的所有权,因为 self 没有 &。这意味着 s1 将被移动到 add 调用中,并且在此之后将不再有效。
综上,s3 = s1 + &s2;
看起来它将复制两个字符串并创建一个新字符串,这个语句实际上获取 s1 的所有权,附加 s2 内容的副本,然后返回结果的所有权。
如果需要连接多个字符串,则 + 操作符的行为会变得笨拙:
let s1 = String::from("tic");let s2 = String::from("tac");let s3 = String::from("toe");let s = s1 + "-" + &s2 + "-" + &s3;
对于以更复杂的方式组合字符串,我们可以使用 format! 宏:
let s1 = String::from("tic");let s2 = String::from("tac");let s3 = String::from("toe");let s = format!("{s1}-{s2}-{s3}");
format! 宏返回一个包含内容的 String。format! 宏使用引用,因此此调用不会获得其任何参数的所有权。
索引到字符串
在许多其他编程语言中,通过索引引用字符串中的单个字符是一种有效且常见的操作。但是,如果尝试在 Rust 中使用索引语法访问 String 的某些部分,则会得到一个错误。
let s1 = String::from("hi");let h = s1[0];
报错:error[E0277]: the type `str` cannot be indexed by `{integer}`
这个要从 Rust 如何在内存中 存储字符串开始讲起。
字符串在内存中的表示
String是Vec<u8>的包装器。
考虑以下两个字符串:
let s1 = String::from("Hola");
let s2 = String::from("Здравствуйте");
s1 的长度是 4 字节。当用 UTF-8 编码时,这些字母中的每个都占 1 字节。然而,s2 的长度不是 12 字节,而是 24 字节。因为该字符串中的每个 Unicode 标量值需要 2 字节的存储空间。
来看一下错误代码:
let hello = "Здравствуйте";
let answer = &hello[0];
当用 UTF-8 编码时,З 的第一个字节是 208,第二个字节是 151,所以看起来答案实际上应该是 208,但是 208 本身并不是一个有效的字符。为了避免返回意外值并导致可能无法立即发现的错误,Rust 根本不编译此代码。
字节、标量值和字形簇
关于 UTF-8 的另一点是,从 Rust 的角度来看,实际上有三种相关的方式来看待字符串:字节、标量值和字形簇(最接近我们称之为字母的东西)。
如果我们看看写在 Devanagari 脚本中的印地语单词 “नमस्ते”,它被存储为 u8 值的向量,看起来像这样:
[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
224, 165, 135]
这是 18 个字节,这就是计算机最终存储这些数据的方式。如果我们把它们看作 Unicode 标量值,也就是 Rust 的 char 类型,这些字节看起来是这样的:
['न', 'म', 'स', '्', 'त', 'े']
Rust 提供了不同的方式来解释计算机存储的原始字符串数据,这样每个程序都可以选择它需要的解释,而不管这些数据是什么人类语言。
Rust 不允许我们索引 String 以获取字符的最后一个原因是,索引操作总是需要常数时间(O(1))。但是不能保证 String 的性能,因为 Rust 必须从头到尾遍历内容,以确定有多少个有效字符。
分割字符串
对字符串进行索引通常不是一个好主意,与其用 [] 索引单个数字,不如用 [] 索引一个范围来创建包含特定字节的字符串切片。
let hello = "Здравствуйте";let s = &hello[0..4];
这里,s 将是一个 &str,它包含字符串的前 4 个字节。前面,我们提到每个字符都是两个字节,这意味着 s 将是 “Зд”。
如果我们尝试用 &hello[0…1], Rust 会在运行时报错,就像在 vector 中访问无效索引一样。
遍历字符串的方法
对字符串片段进行操作的最佳方法是明确说明是需要字符还是字节。对于单个 Unicode 标量值,使用 chars 方法。在 “Зд” 上调用 chars,分离并返回两个 char 类型的值,再遍历。
for c in "Зд".chars() {println!("{c}");
}
程序输出:
З
д
或者,bytes 方法返回每个原始字节。
for b in "Зд".bytes() {println!("{b}");
}
程序输出:
208
151
208
180
一定要记住,有效的 Unicode 标量值可能由多个字节组成。
相关文章:
Rust 数据结构:String
Rust 数据结构:String Rust 数据结构:String什么是字符串?创建新字符串更新字符串将 push_str 和 push 附加到 String 对象后使用 运算符和 format! 宏 索引到字符串字符串在内存中的表示字节、标量值和字形簇 分割字符串遍历字符串的方法 R…...
算法基础 -- 小根堆构建的两种方式:上浮法与下沉法
小根堆构建的两种方式:上浮法与下沉法 在构建小根堆(Min-Heap)时,通常有两种常见的构建方式: 上浮建堆(逐个插入,上浮调整)下沉建堆(Heapify 自底向上,下沉…...
Sprnig MVC 如何统一异常处理 (Exception Handling)?
主要有以下几种方式来实现统一异常处理,其中 ControllerAdvice (或 RestControllerAdvice) 结合 ExceptionHandler 是最常用的方式。 1. ExceptionHandler 注解 作用: 用于标记一个方法,该方法将处理在同一个 Controller 类中抛出的特定类型…...
03、基础入门-SpringBoot的大时代背景
03、基础入门-SpringBoot的大时代背景 # Spring Boot的大时代背景 Spring Boot的出现和发展,与以下时代背景密切相关: ## 1. 微服务架构的兴起 ### 背景 随着互联网应用的复杂度增加,传统的单体架构在扩展性、维护性和团队协作方面遇到瓶…...
【51单片机中断】
目录 配置流程 1.在IE寄存器中开启总中断通道和需要的某中断通道 2.在TCON寄存器开启所用中断的触发方式 3.使用中断函数完成中断 4.若需要中断嵌套则在IP寄存器中配置 5.若需要使用串口的中断,则配置SCON寄存器 6.代码示例 配置流程 1.在IE寄存器中开启总中…...
英飞凌tle9954 GPIO
9 通用输入输出(GPIO) 9.1 功能概述 通用输入 / 输出(GPIO)由输入 / 输出驱动级和端口控制逻辑组成。GPIO 具备以下功能: 输入 / 输出端口功能(PBx) 输出状态可编程,输入状态可读。输出驱动器可编程为推挽、开漏和三态模式。输出驱动器的驱动强度和转换速率(压摆率)可…...
Linux操作系统--进程间通信(system V共享内存)
目录 1.system V共享内存 2.共享内存数据结构 3.共享内存函数 4.实例代码: 1.system V共享内存 共享内存区是最快的IPC(进程间通信)形式。一旦这样的内存映射到共享它的进程地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再…...
力扣HOT100之二叉树:102. 二叉树的层序遍历
这道题太简单了,相当于基础的模板题,但凡涉及到层序遍历一定会用到队列来实现,其他的倒没啥好说的,用两层while循环来层序遍历,外层while循环用于控制访问二叉树的每一层,而内层while循环则负责收割每一层的…...
分布式锁: Redisson 实现分布式锁的原理与技术细节
在分布式系统中,分布式锁是协调多个节点对共享资源访问的核心机制之一。Redis 作为高性能内存数据库,常被用于实现分布式锁,而 Redisson 是 Java 生态中最成熟、功能最丰富的 Redis 客户端之一,其内置的分布式锁实现被广泛应用于生…...
day22-数据结构之 栈队列
一、栈 1.1 栈的基本概念 栈是限定仅在表尾进行插入和删除操作的线性表 栈的特性:先进后出、后进先出 栈顶:允许操作的一端栈底:不允许操作的一端 栈的使用分为入栈,出栈栈分为顺序栈和链式栈 1.2 栈的基本操作 链栈示意图:最好采取头插和头…...
Oracle 批量操作脚本解析:动态执行与分批次删除
一、脚本功能概述 本文分享两段 Oracle PL/SQL 脚本,分别实现动态 SQL 执行和大表分批次删除功能,适用于数据清洗、批量操作优化等场景。通过实际案例演示语法逻辑与使用场景。 二、脚本一:动态 SQL 执行与数据清理 1. 核心逻辑 从临时表t…...
我用 CodeBuddy 开发了一个颜色命名搜索器 —— ColorNameHub 的诞生记
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 在一次整理设计稿配色时,我突然萌生了一个想法:“如果能输入一个颜色代码,就…...
强化学习算法实战:一个例子实现sarsa、dqn、ddqn、qac、a2c、trpo、ppo
简介 在学习强化学习算法:sarsa、dqn、ddqn、qac、a2c、trpo、ppo时,由于有大量数据公式的推导,觉得十分晦涩,且听过就忘记了。 但是当把算法应用于实战时,代码的实现要比数据推导要直观很多。 接下来通过不同的算法实…...
零基础玩转Apache Superset可视化部署
根据官方Quick Start Guide,你可以按照以下步骤进行部署: 1. 确认环境2. 获取代码3. 获取官方最新代码4. 启动服务5. 访问Superset Web界面6. 接入数据源 前提条件: dockerdocker compose 1. 确认环境 安装Docker和Docker Compose 确保你…...
单片机-STM32部分:18、WiFi模组
飞书文档https://x509p6c8to.feishu.cn/wiki/WFmqwImDViDUezkF7ercZuNDnve 一、WiFi模组应用 当设备需要连接网络,实现远程控制,状态监控时,就需要添加通信模组,常见的通信模组WiFi模组、2G模组、4G模组等: 我们的板卡…...
TLS 1.3黑魔法:从协议破解到极致性能调优
一、TLS协议逆向工程实验 1.1 密码学套件破解剧场 实验准备: 靶机:启用TLS 1.2的Nginx服务器 工具集:Wireshark OpenSSL s_client 定制Python脚本 实战攻击复现: # 强制使用弱加密套件连接 openssl s_client -connect exa…...
职业院校物联网安装调试员(工业数智技术)实训解决方案
一、物联网安装调试员 (1)职业定义: 利用检测仪器和专用工具,安装、配置、调试物联网产品与设备的人员。其工作任务就是要搭建数据互联的信息网络,并通过电子标签将真实的物体上网连接,并通过对各类设备的…...
mongodb用systemctl启动code=killed, signal=ABRT
参照在 Ubuntu 上安装 MongoDB Community Edition - 数据库手册 v8.0 - MongoDB Docs 安装后,sudo systemctl start mongod启动失败。 sudo systemctl status mongod 结果: mongod.service - MongoDB Database ServerLoaded: loaded (/lib/systemd/sys…...
基于51单片机和8X8点阵屏、矩阵按键的匹对消除类小游戏
目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、矩阵按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板,用到板上的8X8LED点阵屏和矩阵按键。 【单片机】STC89C52RC 【频率】12T11.0592MHz 效果查看/操…...
智能呼叫系统中的NLP意图理解:核心技术解析与实战
引言:当AI拿起电话时 在智能客服、电话营销等场景中,智能呼叫系统正以每年23%的增长率重塑人机交互方式。而支撑这一变革的核心技术,正是自然语言处理(NLP)中的意图理解模块。本文将深入解析意图理解的技术原理&#…...
信号灯和旋钮在接地电阻柜内的作用主要包括以下几个方面
信号灯的作用: 指示状态:信号灯用于指示接地电阻柜的工作状态,如正常运行、故障报警等。通过不同颜色的灯光(如红色表示故障,绿色表示正常)来提醒操作人员柜子的当前状态,确保及时处理潜…...
MongoDB 应用实战
1. java 原生客户端 引入maven 1 <dependencies> 2 <dependency> 3 <groupId>org.mongodb</groupId> 4 <artifactId>mongodb‐driver‐sync</artifactId> 5 <version>4.1.1</version> 6 </dependency> 7 </depende…...
Java EE初阶——wait 和 notify
1. 线程饥饿 线程饥饿是指一个或多个线程因长期无法获取所需资源(如锁,CPU时间等)而持续处于等待状态,导致其任务无法推进的现象。 典型场景 优先级抢占: 在支持线程优先级的系统中,高优先级线程可能持续…...
SpringBoot--Bean管理详解
Bean管理 Bean扫描 回顾spring: 在XML配置文件中,可以借助 <context:component-scan base-package "com.lyc"> 或者注解 ComponentScan(basePackages"com.lyc") 再springboot项目中,既没有标签,也…...
python爬虫实战训练
前言:哇,今天终于能访问豆瓣了,前几天爬太多次了,网页都不让我访问了(要登录)。 先来个小练习试试手吧! 爬取豆瓣第一页(多页同上篇文章)所有电影的排名、电影名称、星…...
探索大型语言模型(LLM)的开源学习路径:mlabonne/llm-course 深度解析
引言:为什么LLM学习需要系统化课程? 近年来,大型语言模型(Large Language Models, LLMs)彻底改变了自然语言处理领域。从GPT系列到Llama、Mistral等开源模型,掌握LLM的开发和应用能力已成为技术人员的核心竞争力。然而,LLM技术栈涵盖从理论基础到工程实践的复杂内容,如…...
IDEA怎么汉化idea中文改回英文版
第一步:点击左上角的File,然后选择Setting 第二步:Setting页面选择 Appearance & Behavior,然后展开System Settings,然后选择 Language and Region,进行修改 我操作的是2024年的版本 File->Settings -> Ap…...
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
目录 1. 创建Flutter项目 1.1使用Android Studio创建Flutter项目 1.2 使用命令行创建Flutter项目 2. Flutter项目介绍 2.1所有代码都在lib目录下编写 2.1 pubspec.yaml 依赖库/图片的引用 编辑 3. 运行项目 4. 编写mian.dart文件 4.1 使用MaterialApp 和 Scaffold两个组件…...
C++23 中的 ranges::fold_left:范围折叠算法
文章目录 1. **ranges::fold_left 的基本概念**2. **使用示例**示例 1:计算整数范围的和示例 2:计算字符串范围的连接示例 3:使用自定义函数 3. **与其他折叠算法的比较**4. **为什么需要 ranges::fold_left**5. **总结** 随着 C23 的到来&am…...
Vue2项目created不执行
Vue2项目created不执行 设置唯一值 name在 created 调用方法在 watch 中监听路由完整代码示例 设置唯一值 name 在 Vue 组件中,name 属性用于标识组件。确保每个组件的 name 属性是唯一的,这有助于在调试和开发过程中更好地识别组件。 export default …...
mysql的not exists走索引吗
在MySQL中,NOT EXISTS子句是否使用索引取决于子查询中关联字段是否建立了合适的索引。以下是关键点总结: 索引的作用: 当子查询的关联字段(例如B.a_id)存在索引(如普通B-tree索引)时&…...
红黑树实现
1.红黑树的概念 红黑树是一棵二叉搜索树,他的每个节点增加一个存储位来表示节点的颜色,可以是红丝或者黑色。通过对任何一条从根到叶子的路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平…...
将已打包好的aar文件,上传到 Coding 的 Maven 仓库
将已打包好的aar文件,上传到 Coding 的 Maven 仓库。 在android stuio项目的build.gradle 进行上传。 编写代码 plugins {id maven-publish }// 配置要上传的本地 AAR 文件 def aarFile file(D:\\mylibrary-1.0.0.aar)publishing {publications {mavenAar(MavenP…...
海康相机连接测试-极简版
文章目录 1、下载客户端 1、下载客户端 海康机器人官网下载软件 软件下载地址 先下载客户端测试连接 按照你的相机的类型选择客户端 安装完毕后,确保USB线插的是3.0的端口 软件会自动识别相机型号 在上方有播放按钮,可以采集图像信息显示...
深入探索:Core Web Vitals 进阶优化与新兴指标
一、INP(Interaction to Next Paint)深度解析 INP 与 FID 的核心差异 • 响应范围:FID仅测量首次输入延迟,而INP跟踪页面生命周期中所有关键交互 • 测量维度:INP综合考虑输入延迟、处理时间和下一帧渲染时间 • 评…...
AI与产品架构设计系列(2):Agent系统的应用架构与落地实
什么是AI Agent?其在架构中的独特定位 AI Agent(人工智能代理)是一种模拟人类智能行为的自主系统,通常以大型语言模型(LLM)作为核心引擎。简单来说,Agent能够像人一样感知环境信息、规划行动方…...
OpenAI与微软洽谈新融资及IPO,Instagram因TikTok流失四成用户
OpenAI与微软洽谈新融资及IPO 据悉,OpenAI 正与微软洽谈新融资及筹备 IPO,关键问题是微软在 OpenAI 重组后的股权比例。微软已投资超 130 亿美元,双方修订 2019 年合同,微软拟弃部分股权换新技术访问权。OpenAI 上周放弃了有争议转…...
架构篇、第五章_05Jenkins的部署与构建
Linux_架构篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:Jenkins的部署与构建 版本号: 1.0,0 作者: 老王要学习 日期: 2025.05.15 适用环境: Centos7 文档说明 本文档围绕 Jenkins 的部署与构建展开&a…...
`ParameterizedType` 和 `TypeVariable` 的区别
在 Java 的泛型系统中,ParameterizedType 和 TypeVariable 是两个不同的类型表示,它们都属于 java.lang.reflect.Type 接口的子接口。两者都在反射(Reflection)中用于描述泛型信息,但用途和含义不同。 🌟 一…...
HTML 中的 input 标签详解
HTML 中的 input 标签详解 一、基础概念 1. 定义与作用 HTML 中的 <input> 标签是表单元素的核心组件,用于创建各种用户输入字段。作为一个空标签(没有闭合标签),它通过 type 属性来决定呈现何种输入控件,是实…...
从 Vue3 回望 Vue2:性能优化内建化——从黑盒优化到可控编译
文章目录 从 Vue3 回望 Vue2:性能优化内建化——从黑盒优化到可控编译1. 引言2. Vue2 的性能优化机制解析3. Vue3 的编译期优化能力拆解3.1 静态提升(Static Hoisting)3.2 Patch Flag 精确标记3.3 Block Tree (块级更新边界&#…...
HOW - React NextJS 的同构机制
文章目录 一、什么是 Next.js 的同构?二、核心目录结构三、关键函数:如何实现不同渲染方式?1. getServerSideProps —— 实现 SSR(每次请求动态获取数据)2. getStaticProps getStaticPaths —— 实现 SSG(…...
电动汽车直流快充充电桩AEV200-DC240M4的详细介绍
电动汽车直流快充充电桩AEV200-DC240M4产品简介 AEV系列为全新一代分体式电动汽车直流恒功率快速充电机。系统内置 30/40kW 恒功率充电模块,最高输出电压1000V,满足各类车辆充电需求。模块采用隔离风道灌胶设 计 ,可靠性高 ,可应…...
YOLOv7训练时4个类别只出2个类别
正常是4个类别: 但是YOLOv7训练完后预测总是只有两个类别: 而且都是LFM和SFM 我一开始检查了下特征图大小,如果输入是640*640的话,三个尺度特征图是80*80,40*40,20*20;如果输入是416*416的话,三个尺度特征…...
数据赋能(224)——数据与业务协同——数据动态调整原则
概述 数据动态调整原则不仅能帮助组织迅速响应业务需求和技术环境的变化,还能确保数据应用始终与最新的数据处理技术、算法和工具保持同步。通过实施数据动态调整,企业能够更准确地捕捉业务趋势,优化数据质量,以及提高资源利用效…...
Vulfocus靶场-文件上传-3
WSO2 文件上传 (CVE-2022-29464) WSO2是一家成立于 2005 年的开源技术提供商。它提供了一个企业平台,用于在本地和整个 Internet 上 集成应用程序编程接口(API)、应用程序和 Web 服务。 某些 WSO2 产品允许无限制的文件上传和远程代码执行。…...
(for 循环) VS (LINQ) 性能比拼 ——c#
在大多数情况下,for 循环的原始性能会优于 LINQ,尤其是在处理简单遍历、数据筛选或属性提取等场景时。这是由两者的实现机制和抽象层次决定的。以下是具体分析: 一、for 循环与 LINQ 的性能差异原因 1. 抽象层次与执行机制 for 循环&#…...
自学嵌入式 day19-数据结构 链表
二、线性表的链式存储 1.特点: (1)线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占用的位置上。 (2)所以…...
一发入魂:极简解决 SwiftUI 复杂视图未能正确刷新的问题(中)
概述 各位似秃非秃小码农们都知道,在 SwiftUI 中视图是状态的函数,这意味着状态的改变会导致界面被刷新。 但是,对于有些复杂布局的 SwiftUI 视图来说,它们的界面并不能直接映射到对应的状态上去。这就会造成一个问题:状态的改变并没有及时的引起 UI 的变化。 如上图所示…...
UI自动化测试详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 1、about自动化测试 定义:把人为驱动的测试转化为机器执行的一种过程,重点在于持续集成这个概念; 优势:节约人力…...