Rust小技巧 - 让函数既可接受String或&str,也可以返回String或&str

1 场景说明

假设我们有一个函数foo,它既要允许&str也要允许String作为输入参数。或是既要允许&str也要允许String作为输出。&strString之间的转换,我们不希望让调用方来操心。这在开发过程中,是经常遇到的需求。

涉及到的知识点:IntoCow

2 解决方案

2.1 允许不同的输入参数

我们先来让函数接受不同的输入参数,不同的输入参数可以通过给输入参数绑定Into这个trait来实现。输入参数有这个trait,就可以通过.into()方法来将输入参数转化为需要的类型。

str已经实现了Into这个trait,我们可以直接使用。如果是自己定义的类型,则需要自己实现Into。一般自己实现Into会优先实现From这个trait,有了这个trait,Into就自动实现了,这个是完全免费的。

struct MyStr{
    s: String
}

impl From<MyStr> for String{
    fn from(s: MyStr)-> Self{
        s.s
    }
}

fn foo<T: Into<String>>(s: T) -> String{
    s.into()
    // 可以在这里做些操作
}


fn main() {
    let s = "abds";
    let foo_s = foo(s);
    assert_eq!(String::from("abds"), foo_s);
    
    let s = MyStr{
        s: String::from("jojsd")
    };
    let foo_s = foo(s);
    assert_eq!(String::from("jojsd"), foo_s);
}

2.2 允许不同的输出参数

如果我们希望函数返回不同的参数,可以用Cow,这是一个枚举类型,本质是一个智能指针。

pub enum Cow<'a, B> 
where
    B: 'a + ToOwned + ?Sized, 
 {
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

ToOwned表示这个泛型B可以从借用变成拥有所有权,比如&str是借用,没有所有权的,通过to_owned()方法,可以变成String,就有所有权了。?Sized表示这个类型的size是不确定的,比如String的长度是可变的。

use std::borrow::Cow;

fn foo<'a>(s: &'a str) -> Cow<'a, str>{
    if s.len() > 5{
        let s = String::from(&s[0..4]);
        return Cow::Owned(s);
    } else {
        return Cow::Borrowed(s);
    }
    
}

fn main() {
    let s = "adsfadsf";
    let mut foo_s = foo(s);
    assert_eq!("adsf", foo_s.to_mut());
    assert_eq!("adsf".to_string(), foo_s.into_owned());

    let s = "adf";
    let mut foo_s = foo(s);
    assert_eq!("adf", foo_s.to_mut());
    assert_eq!("adf".to_string(), foo_s.into_owned());
}

如果既希望输入不同参数,也希望同时返回不同参数,目前还没有找到特别好的办法。

2.3 让调用方来做处理

如果是不希望输入或者输出是可以转换的,我们最好用&str作为输入和输出,因为&String&str的切片都是&str

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

fn main() {
    let my_string = String::from("hello world");
    let word = first_word(&my_string[..]);
    assert_eq!("hello", word);
    let word = first_word(&my_string);
    assert_eq!("hello", word);

    let my_string_literal = "hello world";
    let word = first_word(&my_string_literal[..]);
    assert_eq!("hello", word);
    let word = first_word(&my_string_literal);
    assert_eq!("hello", word);
}

参考资料

[1] https://doc.rust-lang.org/std/convert/trait.Into.html
[2] https://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html/
[3] https://hermanradtke.com/2015/05/29/creating-a-rust-function-that-returns-string-or-str.html/
[4] https://wiki.jikexueyuan.com/project/rust-primer/intoborrow/cow.html
[5] https://doc.rust-lang.org/book/ch04-03-slices.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Rust中的字符串是一个动态可变的数据类型,称为`String`。通过`String`类型,我们可以创建、修改、操作和处理字符串。 在Rust中,有多种方法可以创建`String`。最常见的方法是使用`format!`宏来构建一个字符串,或者通过`to_string()`方法将其他类型转换为字符串。 ```rust let hello = "Hello".to_string(); let world = String::from("World"); let message = format!("{} {}", hello, world); ``` `String`类型与基本类型(`&str`)之间可以互相转换。使用`&`运算符可以获取`String`类型的引用,而通过使用像`to_string()`这样的方法,可以将`&str`转换为`String`。 ```rust let hello: &str = "Hello"; let hello_string: String = hello.to_string(); ``` 在Rust中,可以使用`+`运算符将两个字符串连接起来,或使用`push_str`方法将一个字符串附加到另一个字符串上。这两种方法都会创建一个新的`String`对象。 ```rust let hello = String::from("Hello"); let world = String::from("World"); let hello_world = hello + &world; // 可以使用&运算符获取world的引用 ``` 当需要修改字符串时,Rust提供了很多有用的方法。我们可以使用`replace`方法来替换子字符串,使用`trim`方法来去除字符串两侧的空格,使用`split`方法将字符串拆分为多个部分等等。 ```rust let message = String::from("Hello World"); let replaced = message.replace("World", "Rust"); let trimmed = message.trim(); let mut parts = message.split_whitespace(); ``` 总之,Rust中的`String`类型提供了许多功能强大且安全的方法来创建、操作和处理字符串。它的灵活性和性能使得在处理字符串时可以更加方便、高效地进行操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七元权

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值