考虑以下示例:
fn main() {
let v: Vec<i32> = vec![1, 2, 3, 4, 5];
let b: i32 = (&v[2]) * 4.0;
println!("product of third value with 4 is {}", b);
}
此操作失败,因为无法将float
与&i32
相乘。
error[E0277]: cannot multiply `{float}` to `&i32`
--> src\main.rs:3:23
|
3 | let b: i32 = (&v[2]) * 4.0;
| ^ no implementation for `&i32 * {float}`
|
= help: the trait `std::ops::Mul<{float}>` is not implemented for `&i32`
但是当我将float更改为int时,它可以正常工作。
fn main() {
let v: Vec<i32> = vec![1, 2, 3, 4, 5];
let b: i32 = (&v[2]) * 4;
println!("product of third value with 4 is {}", b);
}
编译器是否在&i32
和i32
之间实现了操作?
如果是,该操作如何以这种类型安全的语言来证明?
答案 0 :(得分:10)
编译器是否在
&i32
和i32
之间实现了操作?
是的。好吧,不是编译器,而是标准库。您可以看到提示in the documentation。
如果是,那么如何使用这种类型安全的语言来证明此操作的合理性?
“类型安全”不是布尔属性,而是频谱。大多数C ++程序员会说C ++是类型安全的。但是,C ++具有许多自动在类型之间进行转换的功能(构造函数,operator T
,使用值的引用,...)。在设计编程语言时,必须权衡错误的风险(引入方便的类型转换时)与不便(如果没有缺陷时)。
举一个极端的例子:考虑Option<T>
是否会取消引用T
并在None
时惊慌。这就是大多数使用null
的语言的行为。我认为很明显,这种“功能”导致了现实世界中的许多错误(搜索术语“十亿美元的错误”)。另一方面,让我们考虑使用&i32 * i32
进行编译可能导致哪些错误。老实说我没有想到。 Maaaybe 有人想将一个值的原始指针与整数相乘吗?在Rust中不太可能。因此,由于引入带有此功能的错误的机会很小,但是很方便,因此决定实施该功能。
这始终是设计师必须平衡的东西。在此频谱上,不同的语言处于不同的位置。 Rust可能被认为比C ++更具“类型安全”,但毫无疑问,甚至还有比Rust更“类型安全”的语言。在这种情况下,“更安全的类型”仅表示:决策更倾向于“不便而不是潜在的错误”。
答案 1 :(得分:2)
我认为您可能会将锈迹中的'&i32'与C语言中的&var
混淆。
在C中,
int var = 5;
int newvar = &var * 4; /* this would be bad code,
should not multiply an address by an integer!
Of course, C will let you. */
“&”运算符返回变量“ var”的地址。
但是,在锈病中,“&”运算符借用了变量var。
在Rust中,
var: i32 = 5;
assert!(&var * 8, 40);
这有效,因为&var是5,而不是var的地址。请注意,在C中,“&”是运算符。在Rust中,“&”充当变量类型的一部分。因此,类型为&i32。
这非常令人困惑。如果标准键盘上还有更多字符,我相信设计师会使用其他键盘。
请参阅book,并仔细按照图示进行操作。本书中的示例使用String,它在堆上分配。像i32
这样的基元通常在堆上分配,并且可以由编译器完全优化。另外,即使使用引用符号时也会频繁地复制基元,从而造成混乱。尽管如此,我认为先使用String查看堆示例,然后再考虑如何将其应用于基元会更容易。逻辑是相同的,但是实际的存储和优化可能有所不同。
答案 2 :(得分:-1)
实际上非常简单:Rust会自动为您取消引用参考。不同于C,您必须自己取消引用指针。在这方面,Rust引用与C ++引用非常相似。