The Rust Book section on the as operator currently says
The as keyword does basic casting:
let x: i32 = 5;
let y = x as i64;
It only allows certain kinds of casting, however.
What are those certain kinds of allowed casting?
A since-deleted answer here explained that sometimes you need to chain multiple as-casts to accomplish a safe result, that can't be done in a single step. When is that necessary?
I don't think that this is documented very well, but here is some information you might find useful:
A cast e as U is valid if one of the following holds:
e has type T and T coerces to U; coercion-cast
e has type *T, U is *U_0, and either U_0: Sized or
unsize_kind(T) = unsize_kind(U_0); ptr-ptr-cast
e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
e has type T and T and U are any numeric types; numeric-cast
e is a C-like enum and U is an integer type; enum-cast
e has type bool or char and U is an integer; prim-int-cast
e has type u8 and U is char; u8-char-cast
e has type &[T; n] and U is *const T; array-ptr-cast
e is a function pointer type and U has type *T,
while T: Sized; fptr-ptr-cast
e is a function pointer type and U is an integer; fptr-addr-cast
where &.T and *T are references of either mutability,
and where unsize_kind(T) is the kind of the unsize info
in T - the vtable for a trait definition (e.g. fmt::Display or
Iterator, not Iterator<Item=u8>) or a length (or () if T: Sized).
Note that lengths are not adjusted when casting raw slices -
T: *const [u16] as *const [u8] creates a slice that only includes
half of the original memory.
Casting is not transitive, that is, even if e as U1 as U2 is a valid
expression, e as U2 is not necessarily so (in fact it will only be valid if
U1 coerces to U2).
Quoted from The Rustonomicon: Casts
Here's an exhaustive list of all the true casts. For brevity, we will use * to denote either a *const or *mut, and integer to denote any integral primitive:
*T as *U where T, U: Sized
*T as *U TODO: explain unsized situation
*T as integer
integer as *T
number as number
C-like-enum as integer
bool as integer
char as integer
u8 as char
&[T; n] as *const T
fn as *T where T: Sized
fn as integer
Related
I can cast between types by using either from or as:
i64::from(42i32);
42i32 as i64;
What is the difference between those?
as can only be used in a small, fixed set of transformations. The reference documents as:
as can be used to explicitly perform coercions, as
well as the following additional casts. Here *T means either *const T or
*mut T.
Type of e
U
Cast performed by e as U
Integer or Float type
Integer or Float type
Numeric cast
C-like enum
Integer type
Enum cast
bool or char
Integer type
Primitive to integer cast
u8
char
u8 to char cast
*T
*V where V: Sized *
Pointer to pointer cast
*T where T: Sized
Numeric type
Pointer to address cast
Integer type
*V where V: Sized
Address to pointer cast
&[T; n]
*const T
Array to pointer cast
Function item
Function pointer
Function item to function pointer cast
Function item
*V where V: Sized
Function item to pointer cast
Function item
Integer
Function item to address cast
Function pointer
*V where V: Sized
Function pointer to pointer cast
Function pointer
Integer
Function pointer to address cast
Closure **
Function pointer
Closure to function pointer cast
* or T and V are compatible unsized types, e.g., both slices, both the
same trait object.
** only for closures that do not capture (close over) any local variables
Because as is known to the compiler and only valid for certain transformations, it can do certain types of more complicated transformations.
From is a trait, which means that any programmer can implement it for their own types and it is thus able to be applied in more situations. It pairs with Into. TryFrom and TryInto have been stable since Rust 1.34.
Because it's a trait, it can be used in a generic context (fn foo(name: impl Into<String>) { /* ... */ }). This is not possible with as (although see AsPrimitive from the num crate).
When converting between numeric types, one thing to note is that From is only implemented for lossless conversions (e.g. you can convert from i32 to i64 with From, but not the other way around), whereas as works for both lossless and lossy conversions (if the conversion is lossy, it truncates). Thus, if you want to ensure that you don't accidentally perform a lossy conversion, you may prefer using From::from rather than as.
See also:
When should I implement std::convert::From vs std::convert::Into?
From some C legacy code I get a number of constants as int *. In the C++ part, I have an enum of underlying type int. Conversion between the enum and int on a single value basis works. However, conversion between int * and enum * is not possible. See code example below.
Why is that and how would I convert a pointer to some int values to a pointer to int enums and vice versa? I kind of expect it to work since the single value conversions work and the underlying types are the same. I read about What happens if you static_cast invalid value to enum class?
but could not determine if potentially invalid values play a role here.
int i = 3;
enum E : int;
E e;
e = static_cast<E>(i); // ok
i = static_cast<int>(e); // ok
int *j;
E * f;
j = static_cast<int *>(&i); // ok
f = static_cast<E *>(&i); // 'static_cast': cannot convert from 'int *' to 'E *'
j = static_cast<int *>(&e); // 'static_cast': cannot convert from 'E *' to 'int *'
// now use j and f
*j = *f;
Why is that?
From the compiler point of view int* and E* are pointers of different non-related types, that is why static_cast is not applicable here.
How would I convert a pointer to some int values to a pointer to int enums and vice versa?
You might try reinterpret_cast instead of static_cast:
f = reinterpret_cast<E *>(&i);
j = reinterpret_cast<int *>(&e);
From reinterpret_cast:
Any pointer to object of type T1 can be converted to pointer to object of another type cv T2
However, note, that dereferencing f or j (i.e. with *f or *j) will be a violation of the strict aliasing rule (for more details see the discussion below). This means that this kind of conversion, though strictly possible, is usually not useful.
The default 'base type' of an enum is int and can be explicitly specified in the OP. Logically the value stored at E* e where E is an enumeration with base type int is an int. It can't be statically cast.
There's no guarantee in C++ that an enum of base type (say) is layout compatible with short but even if the language tightened up that point there could be issues of type compatibility/
One issue is that E* to int*pi would violate type-safety because pi could be used to quietly set values outside the enumeration.
Similarly int* to E* may violate type safety if the integer value isn't in the enumeration.
Note however the standard makes a clear note that there's nothing to preclude an enum taking a value outside its defined set of values:
This set of values is used to define promotion and conversion semantics for the enumeration type. It does not preclude an
expression of enumeration type from having a value that falls outside this range.
See here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf (see note 95 bottom of p. 156)
The only case that could (if layout-compatibility were assured) be valid is E* to const int* because all values of E* are ints and the value cannot be (correctly) modified through the int * pointer without a further violation of the type system.
But I think the language definition is not that subtle.
I'd like to know in what order are types derived when using auto in C++? For example if I have
auto x = 12.5;
Will that result in a float or a double? Is there any reason it chooses one over the other in terms of speed,efficiency or size? And in what order are the types derived? Does it try int then double then string or is it not that simple?
Thanks
While C++ allows initialization of different typed variables with the same kind of literal, all literals in C++ have one specific type. Therefore the type deduction for auto variables does not need to be special for initialization with literals, it just takes the type of the right hand side (the single, unambiguous type of the literal, in your case) and applies it to the variable.
Examples for literals and their different types:
12.5 //double
12.5f //float
13 //int
13u //unsigned int
13l //long
13ull //unsigned long long
"foo" //char const [4]
'f' //char
So what about float f = 12.5;? Very simple: here the float f is initialized with a literal of type double and an implicit conversion takes place. 12.5 for itself never is float, it always is double.
An exception where the type of the auto variable does not have the type of the literal is when array-to-pointer decay takes place, which is the case for all string literals:
auto c = "bar"; //c has type char const*, while "bar" has type char const[4]
But this again is not special for literals but holds for all kinds of arrays:
int iarr[5] = {};
auto x = iarr; //x has type int*
Assuming I have an int64 variable (or other integer size) representing a valid unicode code-point, and I want to convert it into a rune in Go, what do I do?
In C I would have used a type cast something like:
c = (char) i; // 7 bit ascii only
But in Go, a type assertion won't work:
c, err = rune.( i)
Suggestions?
You just want rune(i). Casting is done via type(x).
Type assertions are something different. You use a type assertion when you need to go from a less specific type (like interface{}) to a more specific one. Additionally, a cast is checked at compile time, where type assertions happen at runtime.
Here's how you use a type assertion:
var (
x interface{}
y int
z string
)
x = 3
// x is now essentially boxed. Its type is interface{}, but it contains an int.
// This is somewhat analogous to the Object type in other languages
// (though not exactly).
y = x.(int) // succeeds
z = x.(string) // compiles, but fails at runtime
In Go, you want to do a conversion.
Conversions
Conversions are expressions of the form T(x) where T is a type and
x is an expression that can be converted to type T.
Conversion = Type "(" Expression ")" .
A non-constant value x can be converted to type T in any of these
cases:
x is assignable to T.
x's type and T have identical underlying types.
x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
x's type and T are both integer or floating point types.
x's type and T are both complex types.
x is an integer or has type []byte or []rune and T is a string type.
x is a string and T is []byte or []rune.
You want to convert x, of type int, int32, or int64, to T of type rune, an alias for type int32. x's type and T are both integer types.
Therefore, T(x) is allowed and is written rune(x), for your example, c = rune(i).
What is Type Conversion and what is Type Casting?
When should I use each of them?
Detail: Sorry if this is an obvious question; I'm new to C++, coming from a ruby background and being used to to_s and to_i and the like.
Conversion is when a value is, um, converted to a different type. The result is a value of the target type, and there are rules for what output value results from what input (of the source type).
For example:
int i = 3;
unsigned int j;
j = i; // the value of "i" is converted to "unsigned int".
The result is the unsigned int value that is equal to i modulo UINT_MAX+1, and this rule is part of the language. So, in this case the value (in English) is still "3", but it's an unsigned int value of 3, which is subtly different from a signed int value of 3.
Note that conversion happened automatically, we just used a signed int value in a position where an unsigned int value is required, and the language defines what that means without us actually saying that we're converting. That's called an "implicit conversion".
"Casting" is an explicit conversion.
For example:
unsigned int k = (unsigned int)i;
long l = long(i);
unsigned int m = static_cast<unsigned int>(i);
are all casts. Specifically, according to 5.4/2 of the standard, k uses a cast-expression, and according to 5.2.3/1, l uses an equivalent thing (except that I've used a different type). m uses a "type conversion operator" (static_cast), but other parts of the standard refer to those as "casts" too.
User-defined types can define "conversion functions" which provide specific rules for converting your type to another type, and single-arg constructors are used in conversions too:
struct Foo {
int a;
Foo(int b) : a(b) {} // single-arg constructor
Foo(int b, int c) : a(b+c) {} // two-arg constructor
operator float () { return float(a); } // conversion function
};
Foo f(3,4); // two-arg constructor
f = static_cast<Foo>(4); // conversion: single-arg constructor is called
float g = f; // conversion: conversion function is called
Classic casting (something like (Bar)foo in C, used in C++ with reinterpret_cast<>) is when the actual memory contents of a variable are assumed to be a variable of a different type. Type conversion (ie. Boost's lexical_cast<> or other user-defined functions which convert types) is when some logic is performed to actually convert a variable from one type to another, like integer to a string, where some code runs to logically form a string out of a given integer.
There is also static and dynamic casting, which are used in inheritance, for instance, to force usage of a parent's member functions on a child's type (dynamic_cast<>), or vice-versa (static_cast<>). Static casting also allows you to perform the typical "implicit" type conversion that occurs when you do something like:
float f = 3.14;
int i = f; //float converted to int by dropping the fraction
which can be rewritten as:
float f = 3.14;
int i = static_cast<int>(f); //same thing
In C++, any expression has a type. when you use an expression of one type (say type S) in a context where a value of another type is required (say type D), the compiler tries to convert the expression from type S to type D. If such an implicit conversion doesn't exist, this results in an error. The word type cast is not standard but is the same as conversion.
E.G.
void f(int x){}
char c;
f(c); //c is converted from char to int.
The conversions are ranked and you can google for promotions vs. conversions for more details.
There are 5 explicit cast operators in C++ static_cast, const_cast, reinterpret_cast and dynamic_cast, and also the C-style cast
Type conversion is when you actually convert a type in another type, for example a string into an integer and vice-versa, a type casting is when the actual content of the memory isn't changed, but the compiler interpret it in a different way.
Type casting indicates you are treating a block of memory differently.
int i = 10;
int* ip = &i;
char* cp = reinterpret_cast<char*>(ip);
if ( *cp == 10 ) // Here, you are treating memory that was declared
{ // as int to be char.
}
Type conversion indicates that you are converting a value from one type to another.
char c = 'A';
int i = c; // This coverts a char to an int.
// Memory used for c is independent of memory
// used for i.