与 Copy 类型的 Borrow 特性相反? [英] Opposite of Borrow trait for Copy types?

查看:64
本文介绍了与 Copy 类型的 Borrow 特性相反?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经看到 Borrow trait 用于定义接受拥有的类型或引用的函数,例如T&T.然后在函数中调用borrow() 方法来获取&T.

I've seen the Borrow trait used to define functions that accept both an owned type or a reference, e.g. T or &T. The borrow() method is then called in the function to obtain &T.

是否有一些特性允许对 T&TT 进行相反的操作)?>复制类型?

Is there some trait that allows the opposite (i.e. a function that accepts T or &T and obtains T) for Copy types?

例如对于这个例子:

use std::borrow::Borrow;

fn foo<T: Borrow<u32>>(value: T) -> u32 {
    *value.borrow()
}

fn main() {
    println!("{}", foo(&5));
    println!("{}", foo(5));
}

这会调用 borrow() 来获取引用,然后立即取消引用.

This calls borrow() to obtain a reference, which is then immediately dereferenced.

是否有另一种实现只在传入 T 时复制值,并在给定 &T 时取消引用?还是上面写这种东西的惯用方式?

Is there another implementation that just copies the value if T was passed in, and dereferences if &T was given? Or is the above the idiomatic way of writing this sort of thing?

推荐答案

Borrow 并没有真正的逆特征,因为它并没有像 那样作为函数的边界真正有用借用是.原因与所有权有关.

There is not really an inverse trait for Borrow, because it's not really useful as a bound on functions the same way Borrow is. The reason has to do with ownership.

考虑一个只需要引用其参数的函数:

Consider a function that only needs to reference its argument:

fn puts(arg: &str) {
    println!("{}", arg);
}

接受 String 在这里会很愚蠢,因为 puts 不需要取得数据的所有权,但接受 &str 意味着我们有时可能会强制调用者将数据保留的时间超过必要的时间:

Accepting String would be silly here, because puts doesn't need to take ownership of the data, but accepting &str means we might sometimes force the caller to keep the data around longer than necessary:

{
    let output = create_some_string();
    output.push_str(some_other_string);
    puts(&output);
    // do some other stuff but never use `output` again
} // `output` isn't dropped until here

问题是output在传递给puts后不需要,调用者知道这一点,但是puts需要引用,所以 output 必须保持活动状态直到块结束.显然,您总是可以通过添加更多块以及有时添加 let 来在调用者中解决此问题,但是 puts 也可以设为通用以让调用者 委托 清理输出的责任:

The problem being that output isn't needed after it's passed to puts, and the caller knows this, but puts requires a reference, so output has to stay alive until the end of the block. Obviously you can always fix this in the caller by adding more blocks and sometimes a let, but puts can also be made generic to let the caller delegate the responsibility of cleaning up output:

fn puts<T: Borrow<str>>(arg: T) {
    println!("{}", arg.borrow());
}

接受 T: Borrow for puts 使调用者可以灵活地决定是保留参数还是将其移动到函数中.¹​​

Accepting T: Borrow for puts gives the caller the flexibility to decide whether to keep the argument around or to move it into the function.¹

现在考虑一个实际需要取得所有权的函数的情况:

Now consider the case of a function that actually needs to take ownership:

struct Wrapper(String);
fn wrap(arg: String) -> Wrapper {
    Wrapper(arg)
}

在这种情况下,接受 &str 将是愚蠢的,因为 wrap 必须在其上调用 to_owned().如果调用者有一个不再使用的 String ,那将不必要地复制可能刚刚移动到函数中的数据.在这种情况下,接受 String 是更灵活的选项,因为它允许调用者决定是进行克隆还是传递现有的 String.有一个反向Borrow";trait 不会增加 arg: String 尚未提供的任何灵活性.

In this case accepting &str would be silly, because wrap would have to call to_owned() on it. If the caller has a String that it's no longer using, that would needlessly copy the data that could have just been moved into the function. In this case, accepting String is the more flexible option, because it allows the caller to decide whether to make a clone or pass an existing String. Having an "inverse Borrow" trait would not add any flexibility that arg: String does not already provide.

但是 String 并不总是最符合人体工程学的参数,因为有几种不同类型的字符串:&strCow<str>, Box<str>... 我们可以让 wrap 更符合人体工程学,说它接受任何可以转换为 的东西字符串.

But String isn't always the most ergonomic argument, because there are several different kinds of string: &str, Cow<str>, Box<str>... We can make wrap a little more ergonomic by saying it accepts anything that can be converted into a String.

fn wrap<T: Into<String>>(arg: T) -> Wrapper {
    Wrapper(arg.into())
}

这意味着您可以像 wrap("hello, world") 一样调用它,而不必在文字上调用 .to_owned() .这不是真正的灵活性胜利——调用者总是可以调用.into()而不失一般性——但它是人体工程学 赢了.

This means you can call it like wrap("hello, world") without having to call .to_owned() on the literal. Which is not really a flexibility win -- the caller can always call .into() instead without loss of generality -- but it is an ergonomic win.

现在,您询问了 Copy 类型.在大多数情况下,上述论点仍然适用.如果您正在编写一个函数,例如 puts,只需要一个 &A,使用 T: Borrow 可能会更多对来电者灵活;对于像 wrap 这样需要整个 A 的函数,只接受 A 更灵活.但是对于 Copy 类型来说,接受 T: Into人体工程学优势就不那么明确了.

Now, you asked about Copy types. For the most part the arguments above still apply. If you're writing a function that, like puts, only needs a &A, using T: Borrow<A> might be more flexible for the caller; for a function like wrap that needs the whole A, it's more flexible to just accept A. But for Copy types the ergonomic advantage of accepting T: Into<A> is much less clear-cut.

  • 对于整数类型,因为泛型会干扰类型推断,所以使用它们通常会减少使用文字的人机工程学;您最终可能不得不显式注释类型.
  • 由于 &u32 没有实现 Into<u32>,所以无论如何这个特殊技巧在这里不起作用.
  • 由于 Copy 类型很容易用作拥有的值,因此首先通过引用使用它们的情况并不常见.
  • 最后,当A: Copy 时,将&A 变成A 就像添加* 一样简单代码>;在大多数情况下,能够跳过这一步可能不足以抵消使用泛型增加的复杂性.
  • For integer types, because generics mess with type inference, using them usually makes it less ergonomic to use literals; you may end up having to explicitly annotate the types.
  • Since &u32 doesn't implement Into<u32>, that particular trick wouldn't work here anyway.
  • Since Copy types are readily available as owned values, it's less common to use them by reference in the first place.
  • Finally, turning a &A into an A when A: Copy is as simple as just adding *; being able to skip that step is probably not a compelling enough win to counterbalance the added complexity of using generics in most cases.

总而言之,foo 几乎肯定应该只接受 value: u32 并让调用者决定如何获取该值.

In conclusion, foo should almost certainly just accept value: u32 and let the caller decide how to get that value.

¹ 对于这个特定的函数,您可能需要 AsRef,因为您不依赖于 Borrow 的额外保证,以及所有T 实现 Borrow 通常与诸如 str 之类的无大小类型相关.但这不是重点.

¹ For this particular function you'd probably want AsRef<str>, because you're not relying on the extra guarantees of Borrow, and the fact that all T implements Borrow<T> isn't usually relevant for unsized types such as str. But that is beside the point.

这篇关于与 Copy 类型的 Borrow 特性相反?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆