SkillsTecnológicas
Menu
Conteúdo da trilha

Retorno de valores

Aprenda como uma função encerra sua execução e entrega resultados ao chamador, garantindo retornos coerentes em todos os caminhos do algoritmo.

Conteúdo 37 de 39

Mecanismo desenhado a lápis recebe duas peças, produz um único valor e o devolve por uma canaleta ao recipiente do chamador

Return entrega um resultado e encerra a execução atual

Uma função recebe dados, executa regras e pode entregar um resultado ao ponto que a chamou. A instrução return — representada como RETORNAR no pseudocódigo — realiza duas ações inseparáveis:

  1. define o valor produzido pela chamada, quando existe uma expressão de retorno;
  2. encerra imediatamente a execução daquela função e devolve o controle ao chamador.

Considere uma regra de desconto:

FUNÇÃO calcularDesconto(subtotal, clientePremium): REAL
  SE subtotal <= 0 ENTÃO
    RETORNAR 0
  FIM SE

  SE clientePremium ENTÃO
    RETORNAR subtotal * 0.10
  FIM SE

  RETORNAR 0
FIM FUNÇÃO

Na chamada abaixo, a expressão subtotal * 0.10 produz 20. Esse valor volta para a variável desconto; então o algoritmo chamador continua na instrução seguinte:

desconto <- calcularDesconto(200, verdadeiro)
total <- 200 - desconto

ESCREVER total  // 180
A chamada calcularDesconto com subtotal 200 entra na função, return devolve 20 ao chamador e impede a execução de qualquer instrução posterior dentro da função
Return termina a função atual; a execução prossegue no chamador, que recebe o resultado da chamada.

Essa saída completa o caminho de entrada estudado em parâmetros e argumentos: argumentos levam valores para dentro da função; o retorno leva um resultado para fora dela.

Comece pelo contrato de saída

Antes de escrever return, pergunte o que o chamador precisa receber. Um contrato de saída claro informa:

  • significado: o número representa desconto em dinheiro, percentual ou preço final?
  • tipo: o resultado é número, texto, valor lógico ou uma estrutura com campos?
  • domínio: quais resultados são válidos e em que unidade?
  • ausência: existe uma resposta legítima quando nenhum resultado é encontrado?
  • falha: entrada inválida vira um resultado, um erro explícito ou outra forma de tratamento?

Para calcularDesconto, podemos declarar: “recebe um subtotal não negativo e informa o desconto monetário, também não negativo”. Isso impede interpretações incompatíveis. Retornar 10 poderia significar R$ 10 ou 10%; o tipo numérico sozinho não resolve a ambiguidade.

O nome da função também participa desse contrato. calcularDesconto sugere que a chamada produz o desconto. Se a intenção fosse produzir o total já descontado, calcularTotalComDesconto seria mais informativo.

Siga o caminho até o primeiro return executado

Uma função pode conter vários return, mas cada chamada alcança no máximo um deles. Assim que um retorno executa, nenhuma instrução posterior do mesmo corpo é alcançada.

function classificarTemperatura(temperatura) {
  if (temperatura < 18) {
    return "fria";
  }

  if (temperatura < 28) {
    return "agradável";
  }

  return "quente";
  console.log("fim"); // nunca executa
}

Para rastrear classificarTemperatura(22):

  1. 22 < 18 resulta em falso;
  2. 22 < 28 resulta em verdadeiro;
  3. return "agradável" encerra a função;
  4. a chamada passa a representar a string "agradável".

A linha depois do último retorno é código inalcançável. Ela não é uma continuação alternativa: não existe caminho capaz de chegar até ela. Se uma ação precisa acontecer em todos os casos, coloque-a antes dos retornos ou reorganize o fluxo conscientemente.

Use retornos antecipados para proteger o caminho principal

Um retorno antecipado pode tratar casos que não devem avançar pelo restante do cálculo. Essas verificações são frequentemente chamadas de cláusulas de guarda:

FUNÇÃO calcularMedia(soma, quantidade): REAL
  SE quantidade <= 0 ENTÃO
    RETORNAR 0
  FIM SE

  RETORNAR soma / quantidade
FIM FUNÇÃO

A guarda evita uma divisão inválida e deixa o caminho principal sem um bloco SENÃO adicional. Isso não significa que toda falha deva virar zero. Aqui, zero só seria correto se o contrato do domínio realmente o aceitasse como resposta para uma coleção vazia. Em outro sistema, a função poderia sinalizar um erro ou representar ausência.

Compare duas organizações equivalentes:

OrganizaçãoVantagem possívelCuidado necessário
retornos antecipadoseliminam casos especiais e reduzem aninhamentomuitos pontos dispersos podem dificultar a auditoria
um retorno ao finalconcentra a saída em um pontopode exigir variável auxiliar e condicionais mais profundas

Não existe uma regra universal que obrigue um único return. Prefira a forma na qual os caminhos e o contrato sejam mais fáceis de ler, testar e manter.

Verifique se todos os caminhos retornam o tipo prometido

O erro mais perigoso nem sempre está na expressão retornada. Pode existir um caminho que não chega a nenhum return:

function calcularDesconto(subtotal, clientePremium) {
  if (subtotal <= 0) {
    return 0;
  }

  if (clientePremium) {
    return subtotal * 0.10;
  }
}

Para calcularDesconto(200, true), o resultado é 20. Para calcularDesconto(200, false), porém, o corpo chega ao fim sem retorno explícito e JavaScript produz undefined. Se o chamador subtrair esse resultado de 200, obterá NaN, propagando a ausência para o cálculo.

A correção é declarar o resultado do caso comum:

function calcularDesconto(subtotal, clientePremium) {
  if (subtotal <= 0) {
    return 0;
  }

  if (clientePremium) {
    return subtotal * 0.10;
  }

  return 0;
}
Comparação entre uma função incompleta, cujo caminho premium retorna 20 mas o caminho comum não retorna valor, e uma função completa, na qual os caminhos retornam respectivamente 20 e zero
Auditar retornos significa seguir cada ramo possível e confirmar que todos entregam um resultado compatível com o contrato.

Linguagens tratam essa situação de modos diferentes. Em Python, chegar ao fim da função equivale a retornar None. JavaScript produz undefined. Já uma função C# declarada com retorno diferente de void precisa impedir que o controle alcance o fim sem retornar um valor. TypeScript também pode verificar caminhos ausentes quando noImplicitReturns está habilitado.

Use as estruturas condicionais para desenhar os ramos e faça uma pergunta em cada folha da árvore: “qual valor esta chamada entrega?”.

Retornar não é o mesmo que exibir

Exibir um dado é produzir um efeito observável. Retornar é entregar um valor ao chamador para que ele decida o próximo uso.

function calcularTotal(preco, quantidade) {
  const total = preco * quantidade;
  console.log(total);
}

const totalDaCompra = calcularTotal(50, 3);
console.log(totalDaCompra); // undefined

A primeira chamada escreve 150 no console, mas não entrega esse número. A variável totalDaCompra recebe undefined. Uma versão reutilizável separa cálculo e apresentação:

function calcularTotal(preco, quantidade) {
  return preco * quantidade;
}

const totalDaCompra = calcularTotal(50, 3);
console.log(totalDaCompra); // 150

Agora o mesmo resultado pode ser exibido, salvo, comparado, enviado para outra função ou usado em uma expressão. A função de cálculo não precisa saber qual destino o chamador escolherá.

Retorno também não é sinônimo de alterar uma variável externa. Uma função que modifica um objeto recebido produz um efeito sobre estado compartilhado; uma função que retorna um valor estabelece um canal explícito de saída. Algumas rotinas fazem ambos, mas essa combinação deve ser intencional e clara.

Use o resultado: guardar, compor ou decidir

Depois do retorno, a chamada funciona como uma expressão cujo valor é o resultado produzido. O chamador pode:

// guardar
desconto <- calcularDesconto(200, verdadeiro)

// compor em outro cálculo
total <- 200 - calcularDesconto(200, verdadeiro)

// usar em uma decisão
SE calcularDesconto(200, verdadeiro) > 0 ENTÃO
  ESCREVER "desconto aplicado"
FIM SE

Guardar primeiro em uma variável intermediária costuma ajudar quando o valor será reutilizado, inspecionado durante a depuração ou nomeado com uma unidade. Compor diretamente pode ser claro para expressões curtas. Evite repetir a chamada apenas para economizar uma variável se ela realiza trabalho relevante ou pode produzir efeitos.

Também é possível ignorar o resultado:

calcularDesconto(200, verdadeiro)

A função executa, mas o valor 20 é descartado. Se ela existe apenas para calcular, essa chamada provavelmente não cumpre nenhum objetivo.

Um retorno estruturado pode preservar resultados relacionados

Às vezes o chamador precisa de mais de uma informação produzida pelo mesmo cálculo. Em vez de recalcular ou depender de variáveis externas, a função pode retornar um único valor estruturado:

function calcularCompra(preco, quantidade, percentualDesconto) {
  const subtotal = preco * quantidade;
  const desconto = subtotal * (percentualDesconto / 100);

  return {
    subtotal,
    desconto,
    total: subtotal - desconto,
  };
}

const resultado = calcularCompra(100, 2, 10);
console.log(resultado.total); // 180

A chamada continua produzindo um valor: nesse caso, um objeto com três campos relacionados. Outras linguagens podem usar registros, classes, estruturas ou tuplas. A sintaxe muda; a decisão lógica permanece: agrupar informações que formam uma resposta coerente.

A função calcularCompra recebe preço e quantidade, retorna um único pacote com subtotal 200, desconto 20 e total 180, e o chamador consulta os campos necessários
Um valor estruturado mantém juntos os resultados do mesmo cálculo e permite que o chamador escolha como utilizá-los.

Não agrupe dados sem relação apenas para “retornar mais coisas”. O formato de saída deve representar um conceito que o restante do algoritmo reconheça.

Diferencie zero, ausência e falha

Quando uma função não encontra um item, qual deve ser o retorno? A resposta depende do contrato:

  • 0 é um resultado válido para uma contagem sem ocorrências;
  • false pode indicar que uma condição não foi satisfeita;
  • null, None ou um tipo opcional pode representar ausência deliberada;
  • um erro explícito pode indicar que a operação não conseguiu produzir uma resposta válida;
  • um objeto de resultado pode separar sucesso, valor e motivo da falha.

Evite usar o mesmo valor para significados incompatíveis. Se uma busca retorna índice 0 para o primeiro elemento, usar 0 também como “não encontrado” torna os casos indistinguíveis. Um marcador de ausência precisa estar fora do conjunto de resultados válidos ou vir acompanhado de informação que remova a ambiguidade.

Esse desenho também depende dos tipos de dados disponíveis. Confirme a convenção da linguagem e documente a regra do domínio em vez de escolher null, zero ou uma exceção por hábito.

Teste o contrato por caminhos, não só por exemplos felizes

Para a função de desconto, uma pequena matriz revela se cada ramo devolve o valor prometido:

SubtotalPremiumResultado esperadoCaminho verificado
200verdadeiro20retorno do desconto de 10%
200falso0retorno do caso comum
0verdadeiro0guarda do limite inferior
-10falso0 ou erro, conforme contratoentrada fora do domínio

Para cada caso, registre:

  1. quais condições foram avaliadas;
  2. qual foi o primeiro return alcançado;
  3. qual tipo e valor chegaram ao chamador;
  4. qual instrução do chamador executou em seguida;
  5. se algum caminho terminou sem resposta explícita.

Um teste manual do algoritmo ajuda a tornar esse percurso visível. A cobertura relevante não é “há um return no arquivo?”, mas “toda entrada prevista pelo contrato chega a uma saída prevista?”.

Pratique fazendo uma auditoria de retorno

Analise a função incompleta:

FUNÇÃO calcularFrete(gratis, subtotal): REAL
  SE gratis ENTÃO
    RETORNAR 0
  FIM SE

  SE subtotal < 100 ENTÃO
    RETORNAR 15
  FIM SE
FIM FUNÇÃO

Faça o exercício:

  1. escreva o contrato de saída em uma frase;
  2. desenhe os caminhos para gratis verdadeiro e falso;
  3. teste subtotais 80, 100 e 150;
  4. identifique qual combinação chega ao fim sem retorno;
  5. adicione a regra explícita para compras de pelo menos 100;
  6. use o valor devolvido para calcular totalComFrete no chamador;
  7. explique por que escrever o frete na tela não substituiria o retorno.

Uma solução possível termina com RETORNAR 0 quando a regra do domínio diz que compras a partir de 100 também têm frete grátis. Se a regra for outra, o valor deve mudar. O importante é que o código não invente silenciosamente a resposta: o contrato vem antes do return.

O que você deve guardar

Return encerra a função atual, entrega o resultado ao chamador e transforma a chamada em um valor que pode ser guardado, combinado ou testado. Exibir, alterar estado e retornar são operações diferentes.

Projete primeiro o contrato de saída, siga todos os ramos e confirme que cada caminho produz um resultado com significado e tipo coerentes. Retornos antecipados podem simplificar casos especiais, e um valor estruturado pode reunir respostas relacionadas sem criar dependências externas. Com a entrada e a saída bem definidas, aprenda a controlar onde cada nome pode ser acessado em escopo de variáveis.

Referências