Result y option
En Rust, Result y Option son tipos de enumeración, lo que significa que contienen valores dentro de sus variantes:
enum Option<T> {
Some(T),
// existence
None, // non-existence
}
enum Result<T, E> {
Ok(T),
// success
Err(E), // failure
}
Result
El resultado es un tipo de enumeración, Result<T, E>, donde tanto T como E son genéricos y representan el éxito y el fracaso. Los tipos de resultados se utilizan a menudo en puntos de entrada y controladores:
Ok(T): un contenedor de resultados que tuvo éxito y que contiene T
Err(E): un contenedor de resultados que ha fallado y que contiene E
Muchos puntos de entrada de contratos se escriben Result<Response, ContractError>, donde Response es la rama Derecha o Éxito, mientras que ContractError es el caso Izquierdo o de falla. Por ejemplo, al observar la base CW20, podemos ver que ejecutar se escribe Result<Response, ContractError>, y la llamada de función que coincide con ExecuteMsg::Transfer sería execute_transfer, que muestra cómo se devuelve el resultado:
pub fn execute_transfer(
deps: DepsMut,
_env: Env,
info: MessageInfo,
recipient: String,
amount: Uint128,
) -> Result<Response, ContractError> {
if amount == Uint128::zero() {
return Err(ContractError::InvalidZeroAmount {});
}
let rcpt_addr = deps.api.addr_validate(&recipient)?;
BALANCES.update(
deps.storage,
&info.sender,
|balance: Option<Uint128>| -> StdResult<_> {
Ok(balance.unwrap_or_default().checked_sub(amount)?)
},
)?;
BALANCES.update(
deps.storage,
&rcpt_addr,
|balance: Option<Uint128>| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) },
)?;
let res = Response::new()
.add_attribute("action", "transfer")
.add_attribute("from", info.sender)
.add_attribute("to", recipient)
.add_attribute("amount", amount);
Ok(res)
}
StdResult
También es importante tener en cuenta StdResult, ya que se utiliza con frecuencia en los controladores de consultas y las funciones que invocan.
Por ejemplo, en el contrato de servicio de nombres, puede observar StdResult, que funciona de manera similar a Result, pero carece de una rama de error específica:
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::ResolveRecord { name } => query_resolver(deps, env, name),
QueryMsg::Config {} => to_binary(&config_read(deps.storage).load()?),
}
}
Veamos la implementación de query_resolver:
fn query_resolver(deps: Deps, _env: Env, name: String) -> StdResult<Binary> {
let key = name.as_bytes();
let address = match resolver_read(deps.storage).may_load(key)? {
Some(record) => Some(String::from(&record.owner)),
None => None,
};
let resp = ResolveRecordResponse { address };
to_binary(&resp)
}
Es esencial hacer coincidir o desenvolver los tipos de contenedores con precisión, lo que le permitirá trabajar con los valores contenidos en ellos.
Option
En Rust, no existe el concepto de nil o null, a diferencia de la mayoría de los demás lenguajes de programación convencionales. En cambio, Rust emplea el tipo Opción, que encapsula la noción de presencia o ausencia dentro de un tipo de contenedor.
La option es un tipo de enumeración, con dos variantes:
Some(): envuelve un valor interno, al que se puede acceder a través de .unwrap(). -None representa la ausencia de un valor y, a menudo, se utiliza como valor predeterminado o de marcador de posición cuando un valor aún no se conoce o no se puede determinar.
El siguiente fragmento de código es un ejemplo de cómo se puede utilizar la option de Rust para permitir valores opcionales de purchase_price y transfer_price:
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
pub purchase_price: Option<Coin>,
pub transfer_price: Option<Coin>,
}
Para los casos del InstantiateMsg anterior, habrá un resultado o nada. Para manejar este escenario, es común usar el operador de coincidencia para hacer coincidir el patrón en los dos casos:
let address = match resolver_read(deps.storage).may_load(key)? {
Some(record) => Some(String::from( & record.owner)),
None => None,
};
Si devolver None indicaría un estado de error, convencionalmente se recomienda generar un error en lugar de manejar el valor Ninguno.
Last updated