Vimos que una vector se puede declarar así:
let array = [10, 20, 30];
let array = [10, 20, 30];
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn inc_width(&mut self, delta: u32) {
self.width += delta;
}
}
fn main() {
let mut rect = Rectangle { width: 10, height: 5 };
println!("old area: {}", rect.area());
rect.inc_width(5);
println!("new area: {}", rect.area());
}
$ cargo run
Compiling hello_cargo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/hello_cargo`
old area: 50
new area: 75
Podemos definir un constructor :
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn new(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
fn area(&self) -> u32 {
self.width * self.height
}
fn inc_width(&mut self, delta: u32) {
self.width += delta;
}
}
fn main() {
let mut rect = Rectangle::new(10, 5);
println!("old area: {}", rect.area());
rect.inc_width(5);
println!("new area: {}", rect.area());
}
fn multiply(x: i16, y: i16) -> i16 {
x * y
}
fn main() {
let x: i8 = 15;
let y: i16 = 1000;
println!("{x} * {y} = {}", multiply(x, y));
}
Si corremos este código:
$ cargo run
Compiling hello_cargo v0.1.0
error[E0308]: mismatched types
--> src/main.rs:9:41
|
9 | println!("{x} * {y} = {}", multiply(x, y));
| -------- ^ expected `i16`, found `i8`
| |
| arguments to this function are incorrect
|
note: function defined here
--> src/main.rs:1:4
|
1 | fn multiply(x: i16, y: i16) -> i16 {
| ^^^^^^^^ ------
help: you can convert an `i8` to an `i16`
|
9 | println!("{x} * {y} = {}", multiply(x.into(), y));
| +++++++
For more information about this error, try `rustc --explain E0308`.
error: could not compile `hello_cargo` due to previous error
Vamos a hacerle caso y vamos a correr : rustc --explain E0308
El tipo esperado no coincide con el tipo recibido.
Ejemplos de código erróneo:
``
fn plus_one(x: i32) -> i32 {
x + 1
}
plus_one("Not a number");
// ^^^^^^^^^^^^^^ expected `i32`, found `&str`
if "Not a bool" {
// ^^^^^^^^^^^^ expected `bool`, found `&str`
}
let x: f32 = "Not a float";
// --- ^^^^^^^^^^^^^ expected `f32`, found `&str`
// |
// expected due to this
```
Este error ocurre cuando se usó una expresión en un lugar donde el compilador esperaba una expresión de un tipo diferente. Puede ocurrir en varios casos, siendo el más común cuando se llama a una función y se pasa un argumento que tiene un tipo diferente al tipo coincidente en la declaración de la función.
Genial! no cambio el tipo de forma implicita.
Todos los tipos enteros de Rust implementan las características From<T> e Into<T> para permitirnos convertir entre ellos. El rasgo From<T> tiene un solo método from() y, de manera similar, el rasgo Into<T> tiene un solo método into(). La implementación de estos rasgos es la forma en que un tipo expresa que se puede convertir en otro tipo.
La biblioteca estándar tiene una implementación de From<i8> para i16, lo que significa que podemos convertir una variable x de tipo i8 en i16 llamando a i16::from(x). O, más simple, con x.into(), porque From<i8> para la implementación de i16 crea automáticamente una implementación de Into<i16> para i8.
Lo mismo se aplica a sus propias implementaciones From para sus propios tipos, por lo que es suficiente implementar solo From para obtener una implementación Into respectiva automáticamente.
Por lo tanto si hacemos .into() este código va funcionar :
fn multiply(x: i16, y: i16) -> i16 {
x * y
}
fn main() {
let x: i8 = 15;
let y: i16 = 1000;
println!("{x} * {y} = {}", multiply(x.into(), y));
}
$ cargo run
Compiling hello_cargo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.83s
Running `target/debug/hello_cargo`
15 * 1000 = 15000
fn pick_one<T>(a: T, b: T) -> T {
if std::process::id() % 2 == 0 { a } else { b }
}
fn main() {
println!("coin toss: {}", pick_one("heads", "tails"));
println!("cash prize: {}", pick_one(500, 1000));
}
/// Determine whether the first argument is divisible by the second argument.
///
/// If the second argument is zero, the result is false.
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
if rhs == 0 {
return false; // Corner case, early return
}
lhs % rhs == 0 // The last expression in a block is the return value
}
Los contenidos se tratan como Markdown. Las librerias de Rust se documentan automáticamente en docs.rs mediante la herramienta rustdoc. Es idiomático documentar todos los elementos públicos en una API utilizando este patrón.
Y para utilizarla podemos hacer :
rustdoc src/main.rs
Una versión de Rust de la famosa pregunta de entrevista de FizzBuzz:
fn main() {
print_fizzbuzz_to(20);
}
fn is_divisible(n: u32, divisor: u32) -> bool {
if divisor == 0 {
return false;
}
n % divisor == 0
}
fn fizzbuzz(n: u32) -> String {
let fizz = if is_divisible(n, 3) { "fizz" } else { "" };
let buzz = if is_divisible(n, 5) { "buzz" } else { "" };
if fizz.is_empty() && buzz.is_empty() {
return format!("{n}");
}
format!("{fizz}{buzz}")
}
fn print_fizzbuzz_to(n: u32) {
for i in 1..=n {
println!("{}", fizzbuzz(i));
}
}
$ cargo run
Compiling hello_cargo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/hello_cargo`
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz
Los parámetros de declaración son seguidos por un tipo (lo contrario de algunos lenguajes de programación), luego un tipo de retorno.
La última expresión en el cuerpo de una función (o cualquier bloque) se convierte en el valor devuelto. Y Simplemente se omite el ; al final de la expresión.
Algunas funciones no tienen valor de retorno y devuelven el 'tipo de unidad', (). El compilador inferirá esto si se omite el tipo de retorno -> ().
La expresión de rango en el ciclo for en print_fizzbuzz_to() contiene =n, lo que hace que incluya el límite superior.
fn main() {
let s1: &str = "World";
println!("s1: {s1}");
let mut s2: String = String::from("Hello ");
println!("s2: {s2}");
s2.push_str(s1);
println!("s2: {s2}");
let s3: &str = &s2[6..];
println!("s3: {s3}");
}
$ cargo run
Compiling hello_cargo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.51s
Running `target/debug/hello_cargo`
s1: World
s2: Hello
s2: Hello World
s3: World
En Rust:
&str introduce un segmento de cadena, que es una referencia inmutable a datos de cadena codificados en UTF-8 almacenados en un bloque de memoria. Los literales de cadena ("Hola") se almacenan en el binario del programa.
El tipo String de Rust es un contenedor alrededor de un vector de bytes. Al igual que con un Vec<T>, es propiedad.
Como ocurre con muchos otros tipos, String::from() crea una cadena a partir de un literal de cadena; String::new() crea una nueva cadena vacía, a la que se pueden agregar datos de cadena usando los métodos push() y push_str().
La macro format!() es una forma conveniente de generar una cadena propia a partir de valores dinámicos. Acepta la misma especificación de formato que println!().
Puede tomar prestados segmentos de &str de String a través de & y, opcionalmente, selección de rango.
Para programadores de C++: piense en &str como const char* de C++, pero el que siempre apunta a una cadena válida en la memoria. Rust String es un equivalente aproximado de std::string de C++ (diferencia principal: solo puede contener bytes codificados en UTF-8 y nunca utilizará una optimización de cadena pequeña).
fn main() {
let a: [i32; 6] = [10, 20, 30, 40, 50, 60];
println!("a: {a:?}");
let s: &[i32] = &a[2..4];
println!("s: {s:?}");
}
$ cargo run
Compiling hello_cargo v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 1.62s
Running `target/debug/hello_cargo`
a: [10, 20, 30, 40, 50, 60]
s: [30, 40]
Creamos un segmento a partir de un arreglo y especificando los índices inicial y final entre paréntesis.
Si el segmento comienza en el índice 0, la sintaxis de rango de Rust nos permite descartar el índice inicial, lo que significa que &a[0..a.len()] y &a[..a.len()] son idénticos.
Lo mismo ocurre con el último índice, por lo que &a[2..a.len()] y &a[2..] son idénticos.
Para crear fácilmente una porción de la matriz completa, podemos usar &a[..].
s es una referencia a una porción de i32s. Y el tipo de s es &[i32] y no menciona la longitud del vector. Esto nos permite realizar cálculos en rebanadas de diferentes tamaños.
Las rebanadas siempre toman prestado de otro objeto. En este ejemplo, a tiene que permanecer "vivo" durante al menos el mismo tiempo que nuestro segmento.
La pregunta sobre la modificación de a[3] puede generar una discusión interesante, pero la respuesta es que, por razones de seguridad de la memoria, no puede hacerlo a través de a después de crear un segmento, pero puede leer los datos de a y s de forma segura.
fn main() {
let ref_x: &i32;
{
let x: i32 = 10;
ref_x = &x;
}
println!("ref_x: {ref_x}");
}
Se dice que una referencia "toma prestado" el valor al que se refiere.
Rust está rastreando la vida útil de todas las referencias para garantizar que vivan lo suficiente. Y lanza el siguiente error:
$ cargo run
Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)
error[E0597]: `x` does not live long enough
--> src/main.rs:9:17
|
9 | ref_x = &x;
| ^^ borrowed value does not live long enough
10 |
11 | }
| - `x` dropped here while still borrowed
12 |
13 | println!("ref_x: {ref_x}");
| ----- borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `hello_cargo` due to previous error
fn main() {
let mut x: i32 = 10;
let ref_x: &mut i32 = &mut x;
*ref_x = 20;
println!("x: {x}");
}
Debemos desreferenciar ref_x al asignarleun valor, de forma similar a los punteros de C y C++.
Rust eliminará automáticamente la referencia en algunos casos, en particular al invocar métodos.
Las referencias que se declaran como mut se pueden vincular a diferentes valores durante su vida útil.
Asegúrese de notar la diferencia entre let mut ref_x: &i32 y let ref_x: &mut i32. El primero representa una referencia mutable que se puede vincular a diferentes valores, mientras que el segundo representa una referencia a un valor mutable.
Los tipos escalares son :
Types | Literals | |
---|---|---|
Signed integers | i8 , i16 , i32 , i64 , i128 , isize | -10 , 0 , 1_000 , 123i64 |
Unsigned integers | u8 , u16 , u32 , u64 , u128 , usize | 0 , 123 , 10u16 |
Floating point numbers | f32 , f64 | 3.14 , -10.0e20 , 2f32 |
Strings | &str | "foo" , "two\nlines" |
Unicode scalar values | char | 'a' , 'α' , '∞' |
Booleans | bool | true , false |
¿Está interesado en aprender un nuevo lenguaje de programación que está creciendo en uso y popularidad? ¡Empiece por aquí! Siente las bases del conocimiento que necesita para compilar programas rápidos y eficaces en Rust.
En esta ruta de aprendizaje, hará lo siguiente:
Sin más dejo link: https://learn.microsoft.com/es-es/training/paths/rust-first-steps/
Como dije, ya tenemos los resultados de la encuesta de stackoverflow!!!
Dejo link:
Si lo comparamos con otros lenguajes :
Experiencia con C o C++: Rust elimina toda una clase de errores de tiempo de ejecución. Obtiene un rendimiento como en C y C++, pero no tiene los problemas de inseguridad de la memoria. Además, es un lenguaje moderno con construcciones como pattern matching y la gestión de dependencias integrada.
Experiencia con Java, Go, Python, JavaScript…: Obtiene la misma seguridad de memoria que en esos lenguajes, además de una sensación de lenguaje de alto nivel similar. Además, obtiene un rendimiento rápido y predecible como C y C++ (sin recolector de basura), así como acceso a hardware de bajo nivel (si lo necesita).