Pular para o conteúdo

Erro E0599 do Rust: no method named X found (método não encontrado)

O E0599 quer dizer que você chamou um método que o Rust não encontra naquele tipo. É muito comum, e a causa costuma ser uma de poucas: um trait que você não importou, um Option que você não abriu, um Vec tratado como se fosse um iterador ou um simples erro de digitação. A boa notícia é que as linhas help: do rustc muitas vezes dizem exatamente o que fazer. Para os outros erros desta série, veja Erros do compilador Rust.

error[E0599]: no method named `write_all` found for struct `Stdout` in the current scope
--> src/main.rs:5:9
|
5 | out.write_all(b"Server started\n").unwrap();
| ^^^^^^^^^
|
= note: the method is available for `Stdout` here
|
= help: items from traits can only be used if the trait is in scope
help: trait `Write` which provides `write_all` is implemented but not in scope; perhaps you want to import it
|
1 + use std::io::Write;
|

Em português: “nenhum método chamado write_all encontrado para a struct Stdout no escopo atual”. O compilador explica que o método vem de um trait, o Write, que não está no escopo, e sugere a linha use que falta.

Este é o programa que gera o erro:

use std::io;
fn main() {
let mut out = io::stdout();
out.write_all(b"Server started\n").unwrap();
}

O ^^^^ marca o nome do método, write_all, e a primeira linha diz em qual tipo o Rust procurou: Stdout. Então a mensagem quer dizer: “olhei em Stdout e não achei nenhum método chamado write_all que eu possa usar aqui”.

O tipo nessa primeira linha é a pista mais útil da mensagem. Se ali aparecer Option<T>, Result<T, E>, Vec<_> ou um parâmetro de tipo T quando você esperava outra coisa, você já sabe qual das correções abaixo precisa.

Em Rust, muitos métodos não pertencem ao tipo em si. Eles vêm de um trait. write_all vem do trait Write, e Stdout o implementa. Só que os métodos de um trait só funcionam num arquivo que tem o trait no escopo, normalmente com uma linha use. É isso que significa “items from traits can only be used if the trait is in scope” (itens de traits só podem ser usados se o trait estiver no escopo).

Essa regra deixa o código previsível. Dois crates podem, cada um, acrescentar um método chamado write_all ao mesmo tipo por meio de traits diferentes. Se todo trait estivesse sempre ativo, não daria para saber a qual deles uma chamada se refere. Com a regra, só contam os traits que você importou.

As outras causas vêm dos tipos rigorosos do Rust. Um Option<&str> não é um &str, então não tem os métodos de &str. Um T genérico pode ser qualquer tipo, então o Rust só deixa você chamar os métodos que os bounds (restrições) de T garantem.

Escolha esta quando o help disser “items from traits can only be used if the trait is in scope”. Acrescente a linha use que o rustc sugere.

use std::io::{self, Write};
fn main() {
let mut out = io::stdout();
out.write_all(b"Server started\n").unwrap();
}

Saída:

Server started
▶ Experimente você mesmo

O texto ficou em inglês de propósito: um literal b"..." só aceita caracteres ASCII, então nada de acentos ali.

O mesmo acontece com o crate rand. Sem o import, rng.random_range(1..=6) falha, e o rustc diz qual trait trazer:

help: trait `RngExt` which provides `random_range` is implemented but not in scope; perhaps you want to import it
|
1 + use rand::RngExt;

Atenção: no rand 0.10 o trait se chama RngExt. Tutoriais mais antigos dizem use rand::Rng, e no rand 0.10 esse import dá exatamente este mesmo erro. Siga a linha help:, não o tutorial.

Correção 2: Tire o valor de dentro do Option ou do Result primeiro

Seção intitulada “Correção 2: Tire o valor de dentro do Option ou do Result primeiro”

Escolha esta quando o tipo no erro for Option<T> ou Result<T, E>. O valor que você quer está lá dentro, e é preciso tirá-lo antes de chamar os métodos dele.

error[E0599]: no method named `to_uppercase` found for enum `Option<T>` in the current scope
--> src/main.rs:4:39
|
4 | println!("First guest: {}", first.to_uppercase());
| ^^^^^^^^^^^^ method not found in `Option<&&str>`

O compilador avisa que o método não existe em Option<&&str>. first() devolve um Option, porque a lista pode estar vazia. O rustc sugere .expect(...), que funciona mas derruba o programa com None. Tratar os dois casos costuma ser melhor:

fn main() {
let guests = vec!["ana", "bo", "chen"];
match guests.first() {
Some(name) => println!("Primeiro convidado: {}", name.to_uppercase()),
None => println!("Ainda não há convidados"),
}
let shout = guests.last().map(|name| name.to_uppercase());
println!("Último convidado: {:?}", shout);
}

Saída:

Primeiro convidado: ANA
Último convidado: Some("CHEN")
▶ Experimente você mesmo

.map() roda o seu código no valor de dentro e mantém o Option em volta. Para um Result (por exemplo, o de .parse()), use match, .unwrap_or(...) ou ? dentro de uma função que retorna Result. Veja Option e Result.

Escolha esta quando você chama .map(), .filter() ou outro método de iterador direto num Vec. Aqui o erro tem outra cara:

error[E0599]: the method `map` exists for struct `Vec<{integer}>`, but its trait bounds were not satisfied
--> src/main.rs:3:35
|
3 | let curved: Vec<i32> = scores.map(|s| s + 5).collect();
| ^^^
|
= note: the following trait bounds were not satisfied:
`Vec<{integer}>: Iterator`

Em português: “o método map existe para Vec<{integer}>, mas os trait bounds dele não foram satisfeitos”. Para alguns métodos, como filter, a mensagem é mais amigável. O rustc diz que o Vec<i32> “is not an iterator” (não é um iterador) e acrescenta help: call `.into_iter()` first. Mesma causa, mesma correção.

Vec<{integer}>: Iterator “não é satisfeito” porque um Vec é uma coleção, não um iterador. Peça um iterador a ele com .iter() (empresta os itens) ou .into_iter() (toma os itens):

fn main() {
let scores = vec![70, 85, 92];
let curved: Vec<i32> = scores.iter().map(|s| s + 5).collect();
println!("{:?}", curved);
}

Saída:

[75, 90, 97]
▶ Experimente você mesmo

Correção 4: Acrescente um trait bound ao T genérico

Seção intitulada “Correção 4: Acrescente um trait bound ao T genérico”

Escolha esta quando o erro disser que o método não foi encontrado “for type parameter T” (para o parâmetro de tipo T). Dentro de uma função genérica, o Rust só sabe o que os bounds de T garantem. Um T sem nada não garante coisa alguma, então não tem métodos.

error[E0599]: no method named `to_string` found for type parameter `T` in the current scope
--> src/main.rs:2:26
|
1 | fn describe<T>(item: T) -> String {
| - method `to_string` not found for this type parameter
2 | format!("[{}]", item.to_string())
| ^^^^^^^^^ method cannot be called on `T` due to unsatisfied trait bounds
|
help: the following trait defines an item `to_string`, perhaps you need to restrict type parameter `T` with it:
|
1 | fn describe<T: ToString>(item: T) -> String {
| ++++++++++

O compilador diz que to_string não pode ser chamado em T e sugere restringir T com o trait ToString. Acrescente o bound sugerido. Agora só são aceitos tipos que podem virar string:

fn describe<T: ToString>(item: T) -> String {
format!("[{}]", item.to_string())
}
fn main() {
println!("{}", describe(42));
println!("{}", describe("pronto"));
}

Saída:

[42]
[pronto]
▶ Experimente você mesmo
  • Um erro de digitação ou um método de outra linguagem. tasks.push_back(...) num Vec dá help: you might have meant to use `push`, e tasks.lenght() dá help: there is a method `len` with a similar name. Correção: use o nome que o rustc sugere.
  • Chamar um Type::new() que você nunca escreveu. O Rust não dá construtores de graça: Account::new("Ana", 100) falha com no associated function or constant named `new` found for struct `Account`. Correção: acrescente impl Account { fn new(owner: &str, balance: i64) -> Self { ... } } ou crie a struct direto com Account { owner: ..., balance: ... }. Veja Structs.
  • Chamar uma função associada com ponto. Se fn new() -> Self não tem parâmetro self, acc.new() falha com “this is an associated function, not a method” (isto é uma função associada, não um método). Correção: chame no tipo, Account::new().

Teste o que aprendeu

1. O rustc diz "items from traits can only be used if the trait is in scope". O que você deve fazer?

2. names.first().to_uppercase() falha com E0599 para Option<&&str>. Por quê?

3. Você chama scores.filter(...) num Vec<i32> e o rustc diz "Vec<i32> is not an iterator". Qual é a correção?

Pontuação: 0 / 3