Type Conversions in C++ and Rust

Xiahua Liu October 19, 2024 #Rust #C++

Both C++ and Rust provide language-level type conversions, but they work very differently. C++ allows implicit conversions driven by constructors and conversion operators, while Rust relies on explicit traits such as Deref, AsRef, From, and Into.

C++ Implicit Type Conversion

Implicit conversions are documented in the standard (see cppreference). The compiler tries to build a value of the required type using constructors or conversion operators. The conversion fails if no viable candidate exists or if overload resolution finds multiple viable candidates.

There are also built-in promotions and narrowing rules for integers and floating-point types.

Something worth noting is the interaction between implicit conversions and overload resolution.

For example, two overloads might both become viable only after conversions, and the compiler will emit an ambiguity error.

However C++ also provides ways to disable implicit conversions. Besides marking constructors as explicit, we can use template deletion so only an exact type is accepted:

template<typename T>
T only_double(T f) = delete;

template<>
double only_double<double>(double f) {
    return f;
}

So when we call this only_double with any parameter type other than double will trigger a compilation error.

double fd=1.0;
float ff=1.0;
only_double(ff); // error: use of deleted function 'T only_double(T) [with T = float]'
only_double(fd);

You can also do explicit conversions via static_cast, reinterpret_cast, and dynamic_cast when you need to opt in consciously.

Conclusion

In general C++ implicit type conversion is very useful and flexible.

The main pitfall is silent changes to integer and floating-point values when a narrowing conversion happens.

Flags like -Wconversion or -Wnarrowing (GCC/Clang) help catch these cases, but it is still a safety hazard, especially for less experienced programmers.

However this type of problem can be found easily with SAST tools like clangd, and I highly suggest any serious C++ programmer should use it while doing their projects.

Rust Type Conversion

Rust provides two mechanisms that participate in borrowing conversions: Deref and AsRef.

There are also 2 common methods to convert the object type without referencing or dereferencing. These 2 are like static_cast in C++.

TraitUsage
Deref&A -> &B
AsRefA.as_ref() -> &B
FromA::from(B) -> A
IntoA.into() -> B

Conclusion

Rust has no general implicit type conversion. Conversions only occur when trait implementations exist, and except for Deref coercions they are opt-in through method calls.

This greatly improves safety, though the trait-based approach can feel more complex than C++ at first.