与 Copy 类型的 Borrow 特性相反? [英] Opposite of Borrow trait for Copy types?
问题描述
我已经看到 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
或 &T
或 T
进行相反的操作)?>复制类型?
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
并不总是最符合人体工程学的参数,因为有几种不同类型的字符串:&str
、Cow<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 implementInto<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 anA
whenA: 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屋!