前言
到目前为止,你已经掌握了不少知识,开始摩拳擦掌想要写一些API
,在你尝试的时候,可能会遇到类似问题:你实现了一个函数,该函数以字符串为参数,代码可能是这样的:
fn my_beautiful_function(arg1: String, arg2: String, arg3: String) {
/* ... */
}
在调用这个接口的时候你需要这么做:
my_beautiful_function(
"first".to_owned(),
"second".to_owned(),
"third".to_owned()
);
这个接口实现的很糟糕,因为每次调用这个接口都需要调用.to_owned()
,可不可以不这么做呢?
正文
&str 与 String
我们需要问自己的是,我们真的需要获取所有权或是借用吗?
借用型
如果你不需要获取数据的所有权,那么将参数设置成引用类型,接下来的问题就变成了:是用&str
还是&String
?答案通常是&str
,让我们看下面这段代码:
fn print_str(msg: &str) {
println!("{}", msg);
}
你可以直接传递一个字符串字面量,你也可以传递一个&String
:
fn main() {
let string_slice = "String slice assigned to variable";
let real_string = "Genuine String".to_owned();
print_str(string_slice);
print_str("Literal slice");
print_str(&real_string);
}
在任何地方你需要一个&str
,那么你也可以直接无障碍的直接使用&String
,但反过来就不行了,如果你将函数的定义改成接收&String
类型的变量,那么当你传递&str
类型参数的时候编译器就会报错
有所有权型
如果你需要一个有所有权的String
,那么需要考虑以下几方面:
- 手动将
&str
转化为String
类型是件麻烦事,这是你希望实现漂亮API
的大障碍 - 应该让接口的使用者决定如何创建有所有权的数据,你不要简单地到处接收
&str
类型的变量然后自己去转换它们
你希望自己的接口能同时处理&str
和String
吗?如果是的话,你需要寻找二者的共同点,下面我们会深入讲解
任意类型
对于一个函数,希望接收一个任意的字符串,我们可以接收一个实现了ToString
方法的参数:
se std::str::FromStr; // Imported to use Ipv4Addr::from_str
fn main() {
let ip_address = std::net::Ipv4Addr::from_str("127.0.0.1").unwrap();
let string_proper = "String proper".to_owned();
let string_slice = "string slice";
needs_string(string_slice);
needs_string("Literal string");
needs_string(string_proper);
needs_string(ip_address);
}
fn needs_string<T: ToString>(almost_string: T) {
let real_string = almost_string.to_string();
println!("{}", real_string);
}
这里涉及到了新的语法,即泛型,上面的needs_string
函数也可以写成这个样子:
fn needs_string(almost_string: impl ToString) {
let real_string = almost_string.to_string();
println!("{}", real_string);
}
那么区别是什么呢?却别就是泛型比impl [trait]
更有力量,Rust
提供了where
关键字可以达到同样的作用:
fn needs_string<T>(string: T)
where
T: ToString,
{
println!("{}", string);
}
where
语法和<T: ToString>
语法没有任何差异
同时接收 &str 或 String
如果你不阅读这部分也不会有太大损失,因为ToString
方法覆盖了大部分情况,不过你也确实让API
的使用者面临着我们在和 教程6 中用.to_string()
将&str
转换为String
同样的问题。很多的结构都实现了Display
,而一个用户可以将大量的对象传递给函数,编译器不会给出错误提示。此外,实现.to_string()
有时会有代价。用户不一定知道API
的实现原理,为此你可能会做很多额外的工作
因为我们已经知道了&String
可以代替&str
,我们可以将输入泛化为任何可以被引用的东西,如一个Str
相比于Borrow<str>
,我们更倾向于AsRef<str>
,前者有更多的前提假设,容易失败,后者假设更少,不易失败。此外还有更多的不同,不过这里我们用不到
下面这段代码和上卖弄的很相似,不过ip_address
不是一个有效的参数,因为他有一个可打印字符串形式并不意味着可以可以被像&str
那样处理
相关阅读
总结
曾经以为Rust
中的字符串很简单,这实在是一种单纯的想法,相较于Rust
,JavaScript
中的字符串简直要简单太多。不过随着学习的深入,你会逐渐领略到Rust
为实现安全性和速度的美妙设计
下一篇文章我们会开启错误处理的学习
更多
- 写给前端看的Rust教程(1)从nvm到rust
- 写给前端看的Rust教程(2)从npm到cargo
- 写给前端看的Rust教程(3)配置Visual Studio Code
- 写给前端看的Rust教程(4)Hello World
- 写给前端看的Rust教程(5)借用&所有权
- 写给前端看的Rust教程(6)String 第一部分
- 写给前端看的Rust教程(7)语言篇[上]
- 写给前端看的Rust教程(8)语言篇[中]
- 写给前端看的Rust教程(9)语言篇[下]
- 写给前端看的Rust教程(10)从 Mixins 到 Traits
- 写给前端看的Rust教程(11)Module
- 写给前端看的Rust教程(12)String 第二部分
- 写给前端看的Rust教程(13)Results & Options