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

时间:2018-04-16 18:56:10

标签: rust

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

  • finally
  • 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>>,
foo(Some("aaa"));

Playground

是否可以使其有效?

1 个答案:

答案 0 :(得分:5)

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

            +-----------+
            |           |
  +---------> Option<B> +----------------------+
  |         |           |                      |
+-+-+       +-----------+          +-----------v----------+
|   |                              |                      |
| A |                              | Option<Cow<'a, str>> |
|   |                              |                      |
+-+-+       +-----------+          +-----------^----------+
  |         |           |                      |
  +---------> Option<C> +----------------------+
            |           |
            +-----------+

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

foo::<&str, _>(Some("aaa"));
foo::<String, _>(Some("aaa".to_string()));
foo::<&str, Option<&str>>(None);

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

  1. 为特定的具体类型(例如From&str等)创建自定义结构并实施Option<String>。传递None仍然会有问题,因为不清楚None的{​​{1}}是什么类型: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>);
    }
    
  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();
    }
    

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

  3. 另见: