Skip to content

Commit 9817877

Browse files
committed
docs: add a chapter about ownership and borrowing
1 parent 7380c5a commit 9817877

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

src/es/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@
1616
- [Casting](./quick-comparisons/casting.md)
1717
- [Nulabilidad y Opcionalidad](./quick-comparisons/nullability-and-optionality.md)
1818
- [Manejo de errores](./quick-comparisons/error-handling.md)
19+
- [Fundamental](./fundamental.md)
20+
- [Ownership y Borrowing](./fundamental/ownership-and-borrowing.md)

src/es/fundamental.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Fundamental
2+
3+
En este capitulo veremos algunos conceptos fundamentales para poder profundizar
4+
un poco más en Rust, sin estos conceptos claves nos encontraremos estancados
5+
con facilidad.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Ownership y Borrowing
2+
3+
Una de las diferencias más profundas entre Go y Rust es cómo se maneja la
4+
memoria.
5+
Mientras que Go confía en un GC (Garbage Collector) para liberar recursos, Rust
6+
no tiene GC.
7+
En su lugar, Rust utiliza un sistema llamado Ownership (Propiedad) y Borrowing
8+
(Prestamos) que le permite liberar memoria automáticamente y de forma segura en
9+
tiempo de compilación.
10+
11+
Puede sonar complejo al principio, pero una vez entendido, notarás que te da más
12+
control, más performance, más seguridad.
13+
14+
## ¿Qué es el Ownership?
15+
16+
En Rust, el *sistema de propiedad* u **Ownership** es un sistema en el cual cada
17+
valor en el programa tiene un "dueño". Ese dueño es, por lo general, una
18+
variable.
19+
Cuando el dueño sale del ámbito de la variable (scope), el valor se libera
20+
automáticamente.
21+
22+
```rust
23+
fn main() {
24+
let texto = "Mundo".to_string(); // texto "posee" el String
25+
println!("Hola {texto}!");
26+
} // Aquí 'texto' sale del scope y libera los recursos utilizados por el String
27+
```
28+
29+
A diferencia de Go, no se necesita un GC para limpiar la memoria, Rust lo hace
30+
al salir del scope.
31+
32+
Otro caso ejemplo podría ser:
33+
34+
```rust
35+
fn saludar(nombre: String) {
36+
println!("Hola, {nombre}");
37+
}
38+
39+
fn main() {
40+
let nombre = "Arquimedes".to_string();
41+
saludar(nombre); // estamos cediendo la propiedad, el ownership
42+
// Eso significa que a partir de aquí ya no es accesible
43+
44+
// Y este println! dara error
45+
println!("Nombre no es valido en esta linea: {nombre}");
46+
}
47+
```
48+
49+
El mensaje de error nos dirá que el valor ha sido movido, eso quiere decir que
50+
movimos el dueño de la variable a la función saludar y por tanto ya no podemos
51+
acceder a la misma variable desde `main`, ahora la variable `nombre` pertenece
52+
al scope de `saludar`, `saludar` se ejecuta y la variable deja de ser accesible.
53+
54+
Veremos un poco más de como es posible que esto suceda más adelante, pero
55+
por ahora quédate con el mecanismo, todo valor tiene un dueño y si movemos el
56+
dueño de scope dejara de ser accesible.
57+
58+
## Borrowing (Prestamos)
59+
60+
En Go puedes pasar referencias a funciones sin pensar demasiado su duración.
61+
En Rust, el mecanismo de referencias se lo llama Borrowing y tiene algunas
62+
reglas como por ejemplo:
63+
- Puedes tener muchas referencias inmutables
64+
- O una sola referencia mutable
65+
- Nunca puedes tener ambas al mismo tiempo
66+
67+
```rust
68+
fn saludar(nombre: &String) {
69+
println!("Hola, {nombre}");
70+
}
71+
72+
fn main() {
73+
let nombre = "Arquimedes".to_string();
74+
saludar(&nombre); // esta siendo prestado, no cedemos la propiedad
75+
76+
println!("Nombre todavía es valido en este contexto: {nombre}");
77+
}
78+
```
79+
80+
A esto se lo considera un borrow, un prestamo, porque estamos prestando el valor
81+
no estamos otorgando la propiedad entera, solo lo prestamos por un tiempo
82+
definido.
83+
84+
En este caso le prestamos el `nombre` a la función `saludar`, `saludar` utiliza
85+
la variable, termina el scope de `saludar` y nos devolverá el valor de `nombre`
86+
al scope original, en este caso `main` siendo aún accesible para el resto del
87+
código que tengamos en la función `main`.
88+
89+
### Borrowing Mutable
90+
91+
Teniendo en cuenta en el ejemplo anterior encontraremos una imposibilidad, como
92+
parte de las reglas, no podremos modificar el valor del nombre desde la función
93+
`saludar`:
94+
95+
```rust
96+
fn saludar(nombre: &String) {
97+
nombre.push_str(" de Siracusa");
98+
println!("Hola, {nombre}");
99+
}
100+
101+
fn main() {
102+
let nombre = "Arquimedes".to_string();
103+
saludar(&nombre); // aunque lo prestemos, no permite modificarse
104+
105+
println!("Nombre todavía es valido en este contexto: {nombre}");
106+
}
107+
```
108+
109+
Y si, quizás teniendo en cuenta lo que vimos acerca de
110+
[Declaración de Variables][variable-declaration] nos demos cuenta de que nos
111+
falta agregar `mut` en la declaración de la variable `nombre`.
112+
113+
```rust
114+
fn saludar(nombre: &String) {
115+
nombre.push_str(" de Siracusa");
116+
println!("Hola, {nombre}");
117+
}
118+
119+
fn main() {
120+
let mut nombre = "Arquimedes".to_string(); // 👈 agregamos mut
121+
saludar(&nombre); // aunque lo prestemos, no permite modificarse
122+
123+
println!("Nombre todavía es valido en este contexto: {nombre}");
124+
}
125+
```
126+
127+
Sin embargo si lo compilamos, encontraremos que tampoco funciona, pero el mismo
128+
error ya nos dará una solución,
129+
130+
```sh
131+
Compiling playground v0.0.1 (/playground)
132+
error[E0596]: cannot borrow `*nombre` as mutable, as it is behind a `&` reference
133+
--> src/main.rs:2:5
134+
|
135+
2 | nombre.push_str(" de Siracusa");
136+
| ^^^^^^ `nombre` is a `&` reference, so the data it refers to cannot be borrowed as mutable
137+
|
138+
help: consider changing this to be a mutable reference
139+
|
140+
1 | fn saludar(nombre: &mut String) {
141+
| +++
142+
```
143+
144+
El error nos dice que estamos modificando la variable `nombre` pero la variable
145+
no ha sido declarada como mutable en la firma de la función.
146+
147+
La solución se muestra en el mismo mensaje de error:
148+
149+
```sh
150+
help: consider changing this to be a mutable reference
151+
|
152+
1 | fn saludar(nombre: &mut String) {
153+
|
154+
```
155+
156+
Modificaremos la firma para que sea declarado como mutable además hay que
157+
agregar enviar el dato a la función de forma mutable:
158+
159+
```rust
160+
fn saludar(nombre: &mut String) { // 👈 Lo recibimos como un prestamo mutable
161+
nombre.push_str(" de Siracusa");
162+
println!("Hola, {nombre}");
163+
}
164+
165+
fn main() {
166+
let mut nombre = "Arquimedes".to_string(); // 👈 agregamos mut
167+
saludar(&mut nombre); // 👈 lo prestamos como mutable
168+
169+
println!("Nombre ha sido modificado: {nombre}");
170+
}
171+
```
172+
173+
De esta forma ahora estaremos haciendo un prestamo mutable.
174+
175+
176+
### 🚫 Reglas Clave
177+
1. En cualquier momento, puedes tener:
178+
- Multiples referencias inmutables, o
179+
- Una sola referencia mutable
180+
2. Los prestamos viven menos tiempo que la variable original
181+
3. Los prestamos no deben causar data races <sup>[1](#note1)</sup>
182+
183+
Estas reglas las verifica el compilador.
184+
185+
### ¿Por qué esto importa?
186+
187+
En Go, muchos bugs se ocultan bajo el GC y la falta de control de ownership:
188+
- Accesos a estructuras que ya no deberías usar
189+
- Ejecuciones inesperadas del GC en código sensible al tiempo <sup>[2](#note2)</sup>
190+
- Bugs dificiles de reproducir por condiciones de carrera
191+
192+
Rust pone estas garantías en tiempo de compilación, indicando los problemas
193+
exactos que tendrás en ejecución y evitando que ocurran.
194+
195+
## Comparaciones rápidas
196+
197+
| Concepto | Go | Rust |
198+
| ---------------------- | ------------------------- | ----------------------------- |
199+
| Liberación de recursos | Garbage Collector | Ownership + Borrowing |
200+
| Copia de valores | Implícita | Explicita (clone) |
201+
| Referencias | Punteros pero sin control | Referencias pero con reglas |
202+
| Seguridad | Depende del Programador | Garantizada por el compilador |
203+
| Coste en tiempo real | GC puede causar pausas | Tiempo determinista |
204+
205+
Seguramente encontraremos más comparaciones a lo largo del libro pero de momento
206+
quedémonos con estas pocas.
207+
208+
---
209+
210+
<sup id="note1">1</sup> Un Data Race o una condición de carrera es un problema
211+
que ocurre en la programación concurrente, no es importante saberlo aún pero
212+
veremos más acerca de esto más adelante.
213+
214+
<sup id="note2">2</sup> Casos similares a estos los podemos encontrar en el
215+
[blog post de Discord][blog-post-discord] donde se explica con lujo de detalle
216+
los problemas de performance que estuvieron sufriendo, en relación a esto mismo,
217+
el GC se activaba de manera frecuente y tenían poco control acerca de esto
218+
debido a que es un proceso automático, es por esto que decidieron migrar a Rust,
219+
este manejo especifico de como funcionan las cosas les permite optimizar sin
220+
miedo código muy complejo.
221+
222+
[variable-declaration]: ./../quick-comparisons/variable-declaration.md#mutabilidad
223+
[blog-post-discord]: https://discord.com/blog/why-discord-is-switching-from-go-to-rust

0 commit comments

Comments
 (0)