Estou trabalhando em uma função que converte um valor JSON em um i32
, retornando um erro se qualquer etapa da conversão falhar.
fn json_to_block_height(height: Value) -> Result<i32, RPCError> {
let err_msg = "Invalid height".to_string();
let height = match height {
Value::Number(h) => Ok(h),
_ => Err(RPCError::CustomError(1, err_msg.clone())),
}?;
let height = height
.as_i64()
.ok_or(RPCError::CustomError(1, err_msg.clone()))?;
if height < 0 {
return Err(RPCError::CustomError(1, err_msg));
}
i32::try_from(height).map_err(|_| RPCError::CustomError(1, err_msg))
}
Existe alguma boa maneira de evitar a clonagem desnecessária da mensagem de erro no match
branch e no ok_or
argumento, além de desenrolar o código inteiro com if...else...
branches explícitos? Tecnicamente, se qualquer uma dessas condições de erro for atendida, o código a seguir será inacessível, então nunca precisaremos realmente mover essa String em um mundo perfeito.
Tentei substituir o ok_or(RPCError::CustomError(1, err_msg.clone())
método por ok_or_else(|| RPCError::CustomError(1, err_msg.clone())
, mas o err_msg ainda parece ser capturado e movido no fechamento.
Pergunta bônus: haveria alguma melhoria de desempenho ao tornar o código mais detalhado com ramificações explícitas para evitar cópias desnecessárias, ou a solução "idiomatic rust" tem mais desempenho apesar dessas cópias?
Clones explícitos podem ser removidos dessa forma, embora eu não espere que isso tenha qualquer implicação no desempenho:
ou o mais funcional:
Além da outra resposta (que mostra como mover em vez de clonar), você deve considerar não criar um
String
antes que o erro ocorra. Agora mesmo, sua função sempre aloca memória para a mensagem de erro, mesmo se a função for bem-sucedida. Usar uma fatia de string primeiro também resolveria seu problema.Além disso, a maneira idiomática é separar o erro e a mensagem inteiramente usando o
Display
trait. Verifique Rust By Example para um exemplo.