【问题标题】:How to make type inference try deref coercion?如何进行类型推断尝试取消引用强制?
【发布时间】:2023-01-08 06:19:04
【问题描述】:

我正在研究(又一个)Advent of Code 框架。因为谜题的解决方案应该保持简单易写,目标是让下面的代码工作未修改的:

/// Sometimes a string is easier to work with.
fn run1(_input: &str) {
}

/// Sometimes bytes are easier or faster.
fn run2(_input: &[u8]) {
}

fn main() {
    run1(&input());
    run2(&input());
}

在这里,input 函数从文件加载输入。因此,它必须返回一个拥有的类型,而不是一个引用。这可以是 Vec<u8>String

为此,我编写了以下 Input 特征:

/// Type of puzzle input that can be loaded: string or raw bytes.
trait Input {
    fn from_bytes(bytes: Vec<u8>) -> Self;
}

impl Input for String {
    fn from_bytes(bytes: Vec<u8>) -> Self {
        String::from_utf8(bytes).unwrap()
    }
}

impl Input for Vec<u8> {
    fn from_bytes(bytes: Vec<u8>) -> Self {
        bytes
    }
}

现在如何定义 input() 函数?天真的签名:

/// Imagine this loads some input from a file.
fn input<I: Input>() -> I {
    I::from_bytes(vec![])
}

这很好,直到我尝试调用它。 run1 采用&amp;str,但&amp;input() 最多只能是&amp;String 类型,导致如下错误:

error[E0277]: the trait bound `str: Input` is not satisfied
  --> src/main.rs:10:11
   |
10 |     run1(&input());
   |           ^^^^^ the trait `Input` is not implemented for `str`
   |
   = help: the trait `Input` is implemented for `String`
note: required by a bound in `input`
  --> src/main.rs:32:13
   |
32 | fn input<I: Input>() -> I {
   |             ^^^^^ required by this bound in `input`

str不能作为input()的返回类型,因为它不是Sized,所以impl Input for str是不可能的。因此,从Stringstr 的强制转换必须发生在input() 函数之外。

我想表达的是,可以将返回类型从String强制转换为str,但是当类型推断发现需要str时,仍然可以推断出I = String。我认为 Deref 特性在这里发挥作用,但我一直无法弄清楚如何用它解决问题。

这可以工作吗,还是我对类型系统的要求太多了?

Playground link

【问题讨论】:

  • 为什么不指定类型参数,即run1(&amp;input::&lt;String&gt;()); run2(&amp;input::&lt;Vec&lt;u8&gt;&gt;());?或者拥有StringVec&lt;u8&gt;
  • 为什么不指定类型参数:因为这需要快速编写,没有人有时间这样做。为什么不采用拥有的价值:因为它不是惯用的 Rust。
  • 我认为您不应该关心 AOC 代码中的惯用 Rust,即用即弃。
  • @ChayimFriedman 出于同样的原因,人们并不都参与 AoC。请允许我自己决定我做什么和不关心什么。
  • 如果你对内存泄漏没问题,你可以让它像这样工作:playground

标签: rust


【解决方案1】:

我可能会将 Input 设为 struct 并为其实现 AsRef&lt;str&gt;AsRef&lt;[u8]&gt; 。 然后通过从&amp;input()input().as_ref() 的小调整,我们可以这样做:

(假设您实际上只想以相同的方式调用run1run2,而不是完全调用run1(&amp;input())

/// Sometimes a string is a useful input.
fn run1(_input: &str) {
}

/// Sometimes bytes are easier.
fn run2(_input: &[u8]) {
}

fn main() {
    run1(input().as_ref());
    run2(input().as_ref());
}

/// Type of puzzle input that can be loaded: string or raw bytes.
struct Input {
    bytes: Vec<u8>
}

impl AsRef<[u8]> for Input {
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}

impl AsRef<str> for Input {
    fn as_ref(&self) -> &str {
        std::str::from_utf8(&self.bytes)
    }
}

/// Imagine this loads some input from a file.
fn input() -> Input {
    Input {bytes: vec![]}
}

【讨论】:

  • 谢谢!我可以接受 as_ref 电话,但如果不需要它会更好。
【解决方案2】:

回答问题的另一面;您无法使用 Deref-coercion 获得所需的行为。

Deref 只能为一个类型实现一次。是的,编译器可以从单个 &amp; 自动解引用多次,但它只会下降一个的实现。如果 str 实现了 Deref&lt;Target = [u8]&gt;,这在理论上是可行的,但遗憾的是它没有。所以这是不可能的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-27
    • 2010-09-09
    • 2019-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-13
    • 2022-11-07
    相关资源
    最近更新 更多