Consider this example:
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);
}
This fails as expected as float
can't be multiplied with &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`
But when I change the float to int, it works fine.
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);
}
Did the compiler implement the operation between &i32
and i32
?
If yes, how is this operation justified in such a type safe language?
Did the compiler implement the operation between
&i32
andi32
?
Yes. Well, not the compiler, but rather the standard library. You can see the impl in the documentation.
If yes, how is this operation justified in such a type safe language?
"Type safe" is not a Boolean property, but rather a spectrum. Most C++ programmers would say that C++ is type safe. Yet, C++ has many features that automatically cast between types (constructors, operator T
, taking references of values, ...). When designing a programming language, one has to balance the risk of bugs (when introducing convenient type conversions) with the inconvenience (when not having them).
As an extreme example: consider if Option<T>
would deref to T
and panic if it was None
. That's the behavior of most languages that have null
. I think it's pretty clear that this "feature" has led to numerous bugs in the real world (search term "billion dollar mistake"). On the other hand, let's consider what bugs could be caused by having &i32 * i32
compile. I honestly can't think of any. Maaaybe someone wanted to multiply the raw pointer of one value with an integer? Rather unlikely in Rust. So since the chance of introducing bugs with this feature is very low, but it is convenient, it was decided to be implemented.
This is always something the designers have to balance. Different languages are in a different position on this spectrum. Rust would likely be considered "more typesafe" than C++, but not doubt, there are even "more typesafe" languages than Rust out there. In this context, "more typesafe" just meant: decisions leaned more towards "inconvenience instead of potential bugs".
I think you may be confusing &i32
from rust with &var
from C.
In 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. */
the '&' operator returns the address of the variable 'var'.
However, in rust, the '&' operator borrows use of the variable var.
In Rust,
let var: i32 = 5;
assert_eq!(&var * 8, 40);
This works, because &var
refers to 5
, not to the address of var
. Note that in C, the &
is an operator. In Rust, the &
is acting as part of the type of the variable. Hence, the type is &i32
.
This is very confusing. If there were more characters left on standard keyboard, i am sure the designers would have used a different one.
Please see the book and carefully follow the diagrams. The examples in the book use String, which is allocated on the heap. Primitives, like i32
are normally allocated on the stack and may be completely optimized away by the compiler. Also, primitives are frequently copied even when reference notation is used, so that gets confusing. Still, I think it is easier to look at the heap examples using String first and then to consider how this would apply to primitives. The logic is the same, but the actual storage and optimization my be different.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With