Выбрать главу

Numeric literals can be type annotated by adding the type as a suffix. As an example, to specify that the literal 42 should have the type i32, write 42i32.

The type of unsuffixed numeric literals will depend on how they are used. If no constraint exists, the compiler will use i32 for integers, and f64 for floating-point numbers.

fn main() {

// Suffixed literals, their types are known at initialization

let x = 1u8;

let y = 2u32;

let z = 3f32;

// Unsuffixed literals, their types depend on how they are used

let i = 1;

let f = 1.0;

// `size_of_val` returns the size of a variable in bytes

println!("size of `x` in bytes: {}", std::mem::size_of_val(&x));

println!("size of `y` in bytes: {}", std::mem::size_of_val(&y));

println!("size of `z` in bytes: {}", std::mem::size_of_val(&z));

println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));

println!("size of `f` in bytes: {}", std::mem::size_of_val(&f));

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

There are some concepts used in the previous code that haven't been explained yet, here's a brief explanation for the impatient readers:

   • std::mem::size_of_val is a function, but called with its full path. Code can be split in logical units called modules. In this case, the size_of_val function is defined in the mem module, and the mem module is defined in the std crate. For more details, see modules and crates.

The type inference engine is pretty smart. It does more than looking at the type of the value expression during an initialization. It also looks at how the variable is used afterwards to infer its type. Here's an advanced example of type inference:

fn main() {

// Because of the annotation, the compiler knows that `elem` has type u8.

let elem = 5u8;

// Create an empty vector (a growable array).

let mut vec = Vec::new();

// At this point the compiler doesn't know the exact type of `vec`, it

// just knows that it's a vector of something (`Vec<_>`).

// Insert `elem` in the vector.

vec.push(elem);

// Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec<u8>`)

// TODO ^ Try commenting out the `vec.push(elem)` line

println!("{:?}", vec);

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

No type annotation of variables was needed, the compiler is happy and so is the programmer!

The type statement can be used to give a new name to an existing type. Types must have UpperCamelCase names, or the compiler will raise a warning. The exception to this rule are the primitive types: usize, f32, etc.

// `NanoSecond` is a new name for `u64`.

type NanoSecond = u64;

type Inch = u64;

// Use an attribute to silence warning.

#[allow(non_camel_case_types)]

type u64_t = u64;

// TODO ^ Try removing the attribute

fn main() {

// `NanoSecond` = `Inch` = `u64_t` = `u64`.

let nanoseconds: NanoSecond = 5 as u64_t;

let inches: Inch = 2 as u64_t;

// Note that type aliases *don't* provide any extra type safety, because

// aliases are *not* new types

println!("{} nanoseconds + {} inches = {} unit?",

nanoseconds,

inches,

nanoseconds + inches);

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

The main use of aliases is to reduce boilerplate; for example the IoResult<T> type is an alias for the Result<T, IoError> type.

Attributes

Primitive types can be converted to each other through casting.

Rust addresses conversion between custom types (i.e., struct and enum) by the use of traits. The generic conversions will use the From and Into traits. However there are more specific ones for the more common cases, in particular when converting to and from Strings.