函数是否可以接受Option或&str或String类型的参数?

syn*_*317 3 rust

我正在尝试创建一个快速,灵活和方便的API,接受可选的字符串参数.我希望用户能够通过:

  • None
  • "foo"
  • "foo".to_string()
  • Some("foo")(相当于"foo")
  • Some("foo".to_string())(相当于"foo".to_string())

据我所知,最好的解决办法来处理"foo"或者"foo".to_string()Into<Cow<'a, str>>.在另一方面,最好的解决方案来处理"foo"或者Some("foo")Into<Option<&'a str>>.

因此我尝试了这个,但它没有编译:

fn foo<'a, T, O>(_bar: O)
where
    T: Into<Cow<'a, str>>,
    O: Into<Option<T>>,
Run Code Online (Sandbox Code Playgroud)
foo(Some("aaa"));
Run Code Online (Sandbox Code Playgroud)
error[E0283]: type annotations required: cannot resolve `_: std::convert::Into<std::borrow::Cow<'_, str>>`
  --> src/main.rs:12:5
   |
12 |     foo(Some("aaa"));
   |     ^^^
   |
note: required by `foo`
  --> src/main.rs:3:1
   |
3  | / fn foo<'a, T, O>(_bar: O)
4  | | where
5  | |     T: Into<Cow<'a, str>>,
6  | |     O: Into<Option<T>>,
7  | | {}
   | |__^
Run Code Online (Sandbox Code Playgroud)

操场

它可以使它工作吗?

She*_*ter 5

我很确定你不能创建这样的功能,但仍然符合人体工程学.问题是泛型类型中可能存在零个,一个或多个潜在路径:

            +-----------+
            |           |
  +---------> Option<B> +----------------------+
  |         |           |                      |
+-+-+       +-----------+          +-----------v----------+
|   |                              |                      |
| A |                              | Option<Cow<'a, str>> |
|   |                              |                      |
+-+-+       +-----------+          +-----------^----------+
  |         |           |                      |
  +---------> Option<C> +----------------------+
            |           |
            +-----------+
Run Code Online (Sandbox Code Playgroud)

这就是你得到错误的原因:不清楚具体类型T应该是什么,因此调用者必须将它提供给编译器.在这里我使用涡轮机:

foo::<&str, _>(Some("aaa"));
foo::<String, _>(Some("aaa".to_string()));
foo::<&str, Option<&str>>(None);
Run Code Online (Sandbox Code Playgroud)

我建议重新评估您的API设计.可能的方向包括:

  1. 创建一个自定义的结构和实施From针对特定的具体类型(例如&str,Option<String>等).传递None仍然会有问题,因为它不清楚None它是什么类型:一个Option<&str>Option<String>

    use std::borrow::Cow;
    
    fn foo<'a, C>(_bar: C)
    where
        C: Into<Config<'a>>,
    {
    }
    
    struct Config<'a>(Option<Cow<'a, str>>);
    
    impl<'a> From<&'a str> for Config<'a> {
        fn from(other: &'a str) -> Config<'a> {
            Config(Some(other.into()))
        }
    }
    
    impl From<String> for Config<'static> {
        fn from(other: String) -> Config<'static> {
            Config(Some(other.into()))
        }
    }
    
    impl<'a> From<Option<&'a str>> for Config<'a> {
        fn from(other: Option<&'a str>) -> Config<'a> {
            Config(other.map(Into::into))
        }
    }
    
    impl From<Option<String>> for Config<'static> {
        fn from(other: Option<String>) -> Config<'static> {
            Config(other.map(Into::into))
        }
    }
    
    fn main() {
        foo("aaa");
        foo("aaa".to_string());
    
        foo(Some("aaa"));
        foo(Some("aaa".to_string()));
        foo(None::<&str>);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 切换到构建器模式 - 我的首选方向:

    use std::borrow::Cow;
    
    #[derive(Debug, Clone, Default)]
    struct Foo<'a> {
        name: Option<Cow<'a, str>>,
    }
    
    impl<'a> Foo<'a> {
        fn new() -> Self {
            Self::default()
        }
    
        fn name<S>(mut self, name: S) -> Self
        where
            S: Into<Cow<'a, str>>,
        {
            self.name = Some(name.into());
            self
        }
    
        fn go(self) {
            println!("The name is {:?}", self.name)
        }
    }
    
    fn main() {
        Foo::new().go();
        Foo::new().name("aaa").go();
        Foo::new().name("aaa".to_string()).go();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,这样就不需要调用者完全指定Some; 使用该name功能意味着存在.如果需要,您可以创建一个without_name函数将其重新设置None.

也可以看看: