Hoy aprenderemos cómo la visibilidad de funciones y la herencia de contratos de Solidity se pueden conceptualizar en Solana. Existen cuatro niveles de visibilidad de funciones en Solidity, que son:
- public - accesible desde dentro del contrato y de forma externa.
- external - accesible solo desde fuera del contrato.
- internal - accesible dentro del contrato y en contratos que lo heredan.
- private - accesible solo dentro del contrato.
Logremos lo mismo en Solana, ¿de acuerdo?
Funciones públicas
Todas las funciones que hemos definido desde el día 1 hasta la fecha son funciones públicas:
pub fn my_public_function(ctx: Context<Initialize>) -> Result<()> {
// Function logic...
Ok(())
}
Añadir la palabra clave pub antes de la declaración de la función hace que la función sea pública.
No puedes eliminar la palabra clave pub para las funciones dentro del módulo (mod) etiquetado con #[program]. No compilará.
No te preocupes demasiado por la distinción entre external y public
Por lo general, es inconveniente para un programa de Solana llamar a su propia función pública. Si hay una función pub en un programa de Solana, a todos los efectos prácticos puedes pensar en ella como external en el contexto de Solidity.
Si deseas llamar a una función pública dentro del mismo programa de Solana, es mucho más fácil envolver la función pública con una función de implementación interna y llamar a esa.
Funciones privadas e internas
Aunque no puedes declarar funciones sin pub dentro del módulo con la macro #[program], puedes declarar funciones dentro del archivo. Considera el siguiente código:
use anchor_lang::prelude::*;
declare_id!("F26bvRaY1ut3TD1NhrXMsKHpssxF2PAUQ7SjZtnrLkaM");
#[program]
pub mod func_test {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// -------- calling a "private" function --------
let u = get_a_num();
msg!("{}", u);
Ok(())
}
}
// ------- We declared a non pub function over here -------
fn get_a_num() -> u64 {
2
}
#[derive(Accounts)]
pub struct Initialize {}
Esto se ejecutará y registrará como se espera.
Esto es todo lo que realmente necesitas saber sobre las funciones públicas e internas si deseas construir programas simples en Solana, pero si quieres organizar tu código mejor que simplemente declarando un montón de funciones en el archivo fuera del programa, puedes seguir leyendo. Rust, y por ende Solana, no tiene “clases” de la forma en que las tiene Solidity, ya que Rust no está orientado a objetos. Por lo tanto, la distinción entre “privado” e “interno” no tiene un análogo directo en Rust.
Rust usa módulos para organizar el código. La visibilidad de las funciones dentro y fuera de estos módulos se discute detalladamente en la sección Visibility and Privacy section of the Rust docs, pero a continuación añadiremos nuestra propia perspectiva orientada a Solana.
Función interna
Esto se puede lograr definiendo la función dentro del módulo del programa y asegurándose de que sea accesible dentro de su propio módulo y de otros módulos donde se importe o se use. Veamos cómo hacer eso:
use anchor_lang::prelude::*;
declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX");
#[program]
pub mod func_visibility {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
// Call the internal_function from within its parent module
some_internal_function::internal_function();
Ok(())
}
pub mod some_internal_function {
pub fn internal_function() {
// Internal function logic...
}
}
}
mod do_something {
// Import func_visibility module
use crate::func_visibility;
pub fn some_func_here() {
// Call the internal_function from outside its parent module
func_visibility::some_internal_function::internal_function();
// Do something else...
}
}
#[derive(Accounts)]
pub struct Initialize {}
Después de compilar el programa, si navegas al archivo ./target/idl/func_visibility.json, observarás que la función definida dentro del módulo some_internal_function no se incluyó en el programa compilado. Esto indica que la función some_internal_function es interna y solo se puede acceder a ella dentro del propio programa y cualquier programa que la importe o use.
Del ejemplo anterior, pudimos acceder a la función internal_function desde dentro de su módulo “padre” (func_visibility) y también desde un módulo separado (do_something) fuera del módulo func_visibility.
Función privada
Definir una función dentro de un módulo específico y asegurarse de que no se exponga fuera de ese ámbito es una forma de lograr visibilidad privada:
use anchor_lang::prelude::*;
declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX");
#[program]
pub mod func_visibility {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
// Call the private_function from within its parent module
some_function_function::private_function();
Ok(())
}
pub mod some_function_function {
pub(in crate::func_visibility) fn private_function() {
// Private function logic...
}
}
}
#[derive(Accounts)]
pub struct Initialize {}
La palabra clave pub(in crate::func_visibility) indica que la función private_function solo es visible dentro del módulo func_visibility.
Pudimos llamar a private_function con éxito en la función initialize porque la función initialize está dentro del módulo func_visibility. Intentemos llamar a private_function desde fuera del módulo:
use anchor_lang::prelude::*;
declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX");
#[program]
pub mod func_visibility {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
// Call the private_function from within its parent module
some_private_function::private_function();
Ok(())
}
pub mod some_private_function {
pub(in crate::func_visibility) fn private_function() {
// Private function logic...
}
}
}
mod do_something {
// Import func_visibility module
use crate::func_visibility;
pub fn some_func_here() {
// Call the private_function from outside its parent module
func_visibility::some_private_function::private_function()
// Do something...
}
}
#[derive(Accounts)]
pub struct Initialize {}
Compila el programa. ¿Qué sucedió? Obtuvimos un error:
❌ error[E0624]: associated function private_function is private
Esto demuestra que private_function no es accesible públicamente y no puede ser invocada desde fuera del módulo donde es visible. Revisa visibility and privacy en la documentación de Rust para más información sobre la palabra clave de visibilidad pub.
Herencia de contratos
La traducción directa de la herencia de contratos de Solidity a Solana no es posible porque Rust no tiene clases.
Sin embargo, una alternativa en Rust implica crear módulos separados que definan funcionalidades específicas y luego usar esos módulos dentro de nuestro programa principal, logrando así algo similar a la herencia de contratos de Solidity.
Obtener módulos desde otro archivo
A medida que los programas se hacen más grandes, por lo general no queremos poner todo en un solo archivo. Así es como podemos organizar la lógica en múltiples archivos.
Vamos a crear otro archivo en la carpeta src llamado calculate.rs y a copiar el código proporcionado en él.
pub fn add(x: u64, y: u64) -> u64 {
// Return the sum of x and y
x + y
}
Esta función add devuelve la suma de x e y.
Y esto, en lib.rs.
use anchor_lang::prelude::*;
// Import `calculate` module or crate
pub mod calculate;
declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX");
#[program]
pub mod func_visibility {
use super::*;
pub fn add_two_numbers(_ctx: Context<Initialize>, x: u64, y: u64) -> Result<()> {
// Call `add` function in calculate.rs
let result = calculate::add(x, y);
msg!("{} + {} = {}", x, y, result);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
En el programa anterior, importamos el módulo calculate que se creó previamente y declaramos una función llamada add_two_numbers que suma dos números y registra el resultado. La función add_two_numbers llama a la función add en el módulo calculate, pasando x e y como argumentos, luego almacena el valor de retorno en la variable result. La macro msg! registra los dos números que se sumaron y el resultado.
Los módulos no tienen que ser archivos separados
El siguiente ejemplo declara un módulo dentro de lib.rs en lugar de calculate.rs.
use anchor_lang::prelude::*;
declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX");
#[program]
pub mod func_visibility {
use super::*;
pub fn add_two_numbers(_ctx: Context<Initialize>, x: u64, y: u64) -> Result<()> {
// Call `add` function in calculate.rs
let result = calculate::add(x, y);
msg!("{} + {} = {}", x, y, result);
Ok(())
}
}
mod calculate {
pub fn add(x: u64, y: u64) -> u64 {
// Return the summation of x and y
x + y
}
}
#[derive(Accounts)]
pub struct Initialize {}
Este programa hace lo mismo que el ejemplo anterior, con la única diferencia de que la función add está presente en el archivo lib.rs y dentro del módulo calculate. Además, añadir la palabra clave pub a una función es crucial, ya que hace que la función sea accesible públicamente. El código a continuación no compilará:
use anchor_lang::prelude::*;
declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX");
#[program]
pub mod func_visibility {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
// Call the private-like function
let result2 = do_something::some_func_here();
msg!("The result is {}", result2);
Ok(())
}
}
mod do_something {
// private-like function. It exists in the code, but not everyone can call it
fn some_func_here() -> u64 {
// Do something...
return 20;
}
}
#[derive(Accounts)]
pub struct Initialize {}
Resumen
En Solidity, pensamos mucho sobre la visibilidad de las funciones porque es muy crítica. Así es como debemos pensar en ello en Rust:
- Funciones públicas / externas (Public / External Functions): Estas son funciones accesibles tanto dentro como fuera del programa. En Solana, todas las funciones declaradas son, por defecto, públicas. Todo en el bloque
#[program]debe ser declarado como pub. - Funciones internas (Internal Functions): Estas son funciones accesibles dentro del propio programa y de los programas que lo heredan. Las funciones dentro de un bloque pub mod anidado no se incluyen en el programa compilado, pero aún así, se puede acceder a ellas dentro o fuera del módulo principal.
- Funciones privadas (Private Functions): Estas son funciones que no son accesibles públicamente y no pueden ser invocadas desde fuera de su módulo. Lograr la visibilidad privada en Rust/Solana implica definir una función dentro de un módulo específico con la palabra clave
pub(in crate::<module>), lo que hace que la función sea visible solo dentro del módulo en el que fue definida.
Solidity logra la herencia de contratos a través de clases, una característica que Rust, el lenguaje utilizado en Solana, no tiene. Sin embargo, aún puedes organizar tu código usando módulos de Rust.
Aprende más con RareSkills
Este tutorial es parte de nuestro Solana course gratuito.
Publicado originalmente el 17 de febrero de 2024