«Есть два способа писать безошибочные программы; работает только третий ».

- Алан Дж. Перлис

Как человек, недавно вошедший в мир программирования, одним из моих первых препятствий было чисто умственное препятствие: ошибки и неудачи - это не плохо. Хотя это и укоренилось в ином (привет, 18+ коллективных лет академической жизни), для меня было важно понять, что вы будете чаще терпеть неудачи, чем добиваться успеха, когда пишете код. Представьте, как вы были бы разочарованы, если бы не изменили свое отношение!

Как я быстро понял, ошибки подскажут, как исправить и написать успешный код, но только, если вы умеете их правильно читать! Даже если вы опытный программист, вам все равно придется отлаживать свой собственный код (не включая код от ваших коллег или других разработчиков), поэтому понимание того, как читать и устранять ошибку, является неотъемлемым навыком на каждом уровне опыта.

В этом блоге мы сначала разберем сам ответ на ошибку, чтобы вы знали, как интерпретировать информацию, а затем рассмотрим наиболее распространенные типы ошибок, которые могут возникнуть при кодировании. Когда у вас будет четкое понимание этих концепций, вы сможете более успешно переработать свой код и, возможно, обнаружите, что перепрыгиваете через ловушки, прежде чем сами сделаете ошибки.

** Вкратце, эта статья будет посвящена тому, как сообщать об ошибках и типы ошибок в Ruby, поскольку это основной язык, который я сейчас изучаю в Flatiron School.

Трассировки стека

Прежде чем мы перейдем к чтению сообщений об ошибках, сначала давайте обсудим на самом базовом уровне, что происходит за кулисами каждый раз, когда вы запускаете свой код. Понимание того, как возникают сами ошибки, будет полезно для фактической интерпретации ошибки и выполнения необходимой детективной работы по ее устранению в вашем коде.

На вашем компьютере есть компилятор: программа, которая переводит ваш код с любого языка программирования, который вы используете в настоящее время, в машинный код или инструкции, которые ваш компьютер может эффективно понять. Каждый раз, когда вы запускаете свою программу, компилятор отправляет переведенные инструкции, строка за строкой, с определенным MO для выполнения инструкций, на компьютер ... который затем возвращает выполненную задачу (или серию задач, в зависимости от сложности вашей программы. ) пользователю.

«Стек вызовов» (иногда именуемый «стеком выполнения», «стеком программы», «стеком управления», «стеком времени выполнения» или «стеком машины»), но чаще всего его называют просто «стеком», представляет собой структуру данных, в которой хранится информация о самой программе и порядке ее операций. Проще говоря, это то, как и когда компьютер выполняет каждый шаг ваших инструкций. Стек состоит из «фреймов стека»: более мелких структур, которые соответствуют отдельному вызову метода, включая его аргументы и переменные, которые еще не завершили свой возврат. Каждый кадр стека строится на кадре перед ним.

introduction.rb
1 def introduction #<=(step 2, reference the method definition)
2   puts “hello, world!” #<=(Step 3, calls #puts method; Step 4, prints string to your console)
3 end
4
5 introduction #<=(step 1, call the #introduction method)
"hello, world!"
=> nil 

В вышеупомянутом методе введения стек будет оценивать каждый шаг в следующем порядке:

Шаг 1. Метод «введение» вызывается в строке 5. (Определение «введение» пропускается до тех пор, пока не будет предложено фактически использовать метод)

Шаг 2. Он затем ссылается на метод «введения», который определен выше в строках 1–3.

Шаг 3. Он вызывает другой метод, «помещает» в «введение», и он должен добавить его в стек - он не может выполнить «введение», не выполнив сначала «помещает».

Шаг 4. Собирает строку «привет, мир!» и выводит на консоль

Трассировка стека (также «обратная трассировка», «обратная трассировка стека» или «обратная трассировка стека») - это читаемая версия стека, когда она выполняет оценки и запускает вашу программу. Трассировка стека для вышеуказанной программы будет выглядеть следующим образом:

introduction.rb:1 in ‘puts’
introduction.rb:1 in ‘introduction’
introduction.rb:5 in ‘<main>’

Давайте рассмотрим это. Во-первых, он ссылается на местоположение - мы находимся в гипотетическом файле Introduction.rb. Если вы читаете снизу вверх: в области ‹main› вашего файла в строке 5 мы вызвали метод Introduction, который начинался в строке 1, что требовало нам также вызвать метод put. Поскольку метод Put вызывается внутри метода Introduction, обе строки отчета 1.

В случае ошибки сообщение об ошибке будет включать трассировку стека, чтобы помочь вам точно определить источник и, что наиболее полезно, местоположение ошибки. При использовании более сложных методов, особенно тех, которые могут ссылаться на несколько методов, которые вы определили в другом месте, ваша трассировка стека может занять довольно много времени, и вам придется пройти по вашему коду назад, чтобы определить строку, в которой ваш код был написан неправильно.

Чтение сообщений об ошибках

Каждое сообщение об ошибке Ruby состоит из трех важных элементов: типа, местоположения и описания.

introduction.rb
1 def introduction
2   puts “hello, world!”
3 end
4
5 intro
NameError: undefined local variable or method `intro’ for main:Object
Did you mean? introduction
from introduction.rb:5 in `<main>’

В приведенном выше ответе об ошибке тип ошибки - NameError.

Расположение: Introduction.rb: 5 в ‹main›. Чтобы разбить это дальше, ошибка указывает на расположение файла - в данном случае наш Introduction.rb - и, в частности, отмечает, что ошибка произошла в строке 5 в главном файле ‹ › объем.

description - это неопределенная локальная переменная или метод "intro".

Часто ошибка также предлагает решение - в данном случае Вы имели в виду? введение. Компилятор распознает, что у вас есть метод, определенный с именем, аналогичным имени intro, и предлагает вместо этого вызвать метод Introduction. В данном случае это совершенно правильно. Нам нужно вызвать Introduction вместо intro, поскольку мы не определили ни одного метода с именем intro. Как только мы внесем эту настройку в строку 5, консоль напечатает:

1 def introduction
2   puts “hello, world!”
3 end
4
5 introduction
“hello, world!”
=> nil 

Большой! Теперь мы успешно прочитали сообщение об ошибке для подсказок, определили конкретную ошибку в нашем коде и применили решение, чтобы наш код мог успешно работать!

Типы распространенных ошибок

«При отладке новички вставляют корректирующий код; специалисты удаляют дефектный код ».

- Ричард Паттис

Если мы воспользуемся советом Ричарда Паттиса, цель чтения кодов ошибок всегда должна заключаться в том, чтобы пересмотреть наш исходный код, чтобы создать эффективный код, который выполняет наши намеченные задачи. Следующие типы ошибок - это те, которые я обнаружил наиболее распространенными в ходе моих исследований и моего собственного опыта программирования.

Имя Ошибки

NameError возникает, когда объект в вашем коде не определен или недействителен. По моему опыту, эти исключения возникают, когда я не сосредоточен или пишу код слишком быстро. Давайте посмотрим на пример. здесь мы хотим вывести nemo на консоль.

# rubyerrors.rb
1 puts nemo
NameError: undefined local variable or method `nemo’ for main:Object
from rubyerrors.rb:1:in `<main>’

В приведенном выше примере NameError указывает на строку как на неопределенную переменную или метод. Поскольку Ruby - это объектно-ориентированный язык программирования, он обычно предполагает, что каждый элемент является объектом и, следовательно, должен быть определен. Чтобы поместить строку, правильный синтаксис здесь - «строка».

Чтобы устранить эту ошибку, мы можем провести рефакторинг нашего кода в соответствии с контекстом.

# rubyerrors.rb
1 puts “nemo”
=> “nemo”

Ошибки NoMethod

Подклассом NameError является NoMethodError. Ошибка NoMethodError возникает, когда для объекта вызывается неопределенный метод. Возможно, вы еще не написали этот метод - ой! - или вы указали неправильное имя метода (как мы видели выше в примере выше, в разделе «Чтение сообщений об ошибках»). В этом примере давайте напечатаем случайное хвастовство Гастоном.

# rubyerrors.rb
...
3 boast_gaston
NoMethodError: undefined method `boast_gaston’ for main:Object
from rubyerrors.rb:3:in `<main>’

Здесь мы видим, что мы вызываем метод с именем boast, который еще не определен. Чтобы исправить это, мы должны добавить этот метод в наш код и затем вызвать метод. Давайте изменим наш код, чтобы отразить это:

# rubyerrors.rb
...
3 def boast_gaston
4   boasts = [“No one\’s slick as Gaston”, “No one\’s quick as Gaston”, “No one\’s neck\’s as incredibly thick as Gaston”, “No one\’s got a swell cleft in his chin like Gaston”, “No one fights like Gaston”, “In a wrestling match nobody bites like Gaston”, “No one shoots like Gaston”, “In a spitting match nobody spits like Gaston”]
5   puts boasts.sample(1)
6 end
7
8 boast_gaston
No one’s got a swell cleft in his chin like Gaston
=> nil

Здесь вы можете видеть, что мы написали метод, который использует метод .sample для выбора элемента (1) из массива options, а затем ставит его на консоль. Он возвращает nil, потому что мы не просили метод на самом деле вернуть что-либо. (NB: вы также можете вызвать put boast.sample без (1), чтобы вернуть только один случайный элемент из вашего массива. Однако, если вы хотите вызвать два случайных элемента, вам нужно будет вызвать put boast.sample (2))

Если бы мы хотели этого, мы могли бы снова исправить:

# rubyerrors.rb
...
3 def boast_gaston
4   boasts = [“No one\’s slick as Gaston”, “No one\’s quick as Gaston”, “No one\’s neck\’s as incredibly thick as Gaston”, “No one\’s got a swell cleft in his chin like Gaston”, “No one fights like Gaston”, “In a wrestling match nobody bites like Gaston”, “No one shoots like Gaston”, “In a spitting match nobody spits like Gaston”]
5   return boasts.sample(1)
6 end
7
8 boast_gaston
=> [“In a spitting match nobody spits like Gaston”]

Ошибки синтаксиса

SyntaxError - это когда ваш синтаксис не соответствует правилам вашего конкретного языка программирования. Моя самая распространенная синтаксическая ошибка Ruby - это когда я теряю конец в своем коде. Давайте составим расписание королевского турне Жасмин от имени Аграбы.

# rubyerrors.rb
...
10 def royaltour(princess)
11   puts “#{princess} is going to:\n”
12   locations = [“Beirut”, “Abu Dhabi”, “Cairo”, “Riyadh”, “Baghdad”, “Tehran”, “Alexandria”]
13   locations.each_with_index {|city, index| puts “#{index+1}. #{city}”
14 end
15
16 royaltour(“Jasmine”)
SyntaxError: unexpected keyword_end, expecting ‘}’ for main:Object
from rubyerrors.rb:14:in `<main>’

Если мы проанализируем сообщение об ошибке, мы увидим, что тип ошибки - SyntaxError: что-то в нашем коде не соответствует ожиданиям Ruby. Ошибка находится в строке 14. Сообщение означает, что конец в строке 14 является неожиданным, но ожидалось появление закрывающей фигурной скобки (вы также можете услышать это называется «скобка»). Ошибка здесь намекает на то, что нам не хватает } прежде, чем мы дойдем до конца в четырнадцатой строке. Итак, если бы мы пересмотрели наш код, чтобы устранить эту ошибку, он бы выглядел следующим образом:

# rubyerrors.rb
...
10 def royaltour(princess)
11   puts “#{princess} is going to:\n”
12   locations = [“Beirut”, “Abu Dhabi”, “Cairo”, “Riyadh”, “Baghdad”, “Tehran”, “Alexandria”]
13   locations.each_with_index {|city, index| puts “#{index+1}. #{city}”}
14 end
15
16 royaltour(“Jasmine”)
Jasmine is going to:
1. Beirut
2. Abu Dhabi
3. Cairo
4. Riyadh
5. Baghdad
6. Tehran
7. Alexandria
=> [“Beirut”, “Abu Dhabi”, “Cairo”, “Riyadh”, “Baghdad”, “Tehran”, “Alexandria”]

Тип ошибки

Ошибка TypeError возникает, когда объект не является ожидаемым типом данных для вашего метода. Типичный пример этого - когда вы пытаетесь составить математическое уравнение для строки. В нашем примере давайте возьмем Семь гномов и спросим, ​​какие из них сейчас находятся в шахте.

# rubyerrors.rb
...
18 dwarfs = [“Doc”, “Grumpy”, “Happy”, “Sleepy”, “Bashful”, “Sneezy”, “Dopey”]
19
20 dwarfs.sample(“4”)
TypeError: no implicit conversion of String into Integer
from rubyerrors.rb:20:in `sample’

Здесь мы видим, что мы пытались вызвать метод .sample, который требует аргумента Fixnum (т. Е. Целого числа), но передается в виде строки . Поскольку тип аргумента не соответствует требуемому, возникло исключение TypeError. Чтобы исправить это, нам нужно просто изменить нашу строку на fixnum:

# rubyerrors.rb
...
18 dwarfs = [“Doc”, “Grumpy”, “Happy”, “Sleepy”, “Bashful”, “Sneezy”, “Dopey”]
19
20 dwarfs.sample(4)
=> [“Sneezy”, “Dopey”, “Sleepy”, “Happy”]

Ошибки аргумента

Ошибка ArgumentError довольно очевидна - код требует другого количества аргументов, чем вы передали, либо меньше, либо больше. Однако другой способ вызвать это исключение - передать аргумент, который не является допустимым параметром, что приведет к возникновению того же исключения. В этом последнем примере давайте спросим Мулан, не является ли один из ее друзей другом.

# rubyerrors.rb
...
22 def mulan
23   if [“Mushu”, “Cri-Kee”].include? name
24     puts “Yes, #{name} is my sidekick.”
25   else
26     puts “No, #{name} is not my sidekick.”
27   end
28 end
29
30 mulan(“Ling”)
ArgumentError: wrong number of arguments (given 1, expected 0)
from rubyerrors.rb:22:in `mulan’
from rubyerrors.rb:30:in `<main>’

В этой ArgumentError мы видим, что ошибка возникла в строке 30, когда мы вызывали mulan («Ling»). Строка 30 выше ссылается на строку 22 метода mulan, который включен в трассировку стека. Мы дали методу mulan аргумент («Ling») в строке 30, но метод mulan ничего не ожидал!

В исходном определении метода mulan мы не позволяли методу передавать аргумент, который представлен как («Ling») в строке 30. Мы однако можно увидеть, что загадочный объект, имя, довольно часто встречается в методе… а Ling - это имя. Таким образом, мы должны прийти к осознанию того, что нам нужно передать мулан аргумент, который должен называться имя, чтобы метод мог определить, является ли друг (идентифицированный по его имени ) квалифицируется как ее закадычный друг. Единственные помощники Мулан здесь - Мушу и Кри-Ки. Чтобы изменить код, чтобы он работал правильно, нам нужно разрешить аргумент в строке 22.

# rubyerrors.rb
...
22 def mulan(name)
23   if [“Mushu”, “Cri-Kee”].include? name
24     puts “Yes, #{name} is my sidekick”
25   else
26   puts “No, #{name} is not my sidekick”
27   end
28 end
29
30 mulan(“Ling”)
No, Ling is not my sidekick
=> nil

Теперь, когда мы рассмотрели, как читать сообщения об ошибках и наиболее распространенные типы ошибок Ruby, вы должны иметь возможность анализировать подсказки из сообщений об ошибках, которые будут возникать для вашего кода в будущем. Мой лучший совет - когда вы в тупике, прочитайте все сообщение об ошибке дважды… а затем обратитесь к своему верному другу, Google. StackOverflow - один из моих любимых источников помощи, а также страницы Ruby-Doc.

Удачи и счастливого кодирования!

Источники

Ruby-Doc: Exception

Ruby-Doc: NameError

Ruby-Doc: NoMethodError

Ruby-Doc: SyntaxError

Ruby-Doc: TypeError

Ruby-Doc: ArgumentError

Википедия: Стек вызовов

AppSignal: чтение и понимание трассировки стека

ExceptionalCreatures: NoMethodError