Visibility

Methods are public by default: the compiler will always let you invoke them. There is no public keyword for this reason.

Methods can be marked as private or protected.

Private methods

A private method can only be invoked without a receiver, that is, without something before the dot. The only exception is self as a receiver:

  1. class Person
  2. private def say(message)
  3. puts message
  4. end
  5. def say_hello
  6. say "hello" # OK, no receiver
  7. self.say "hello" # OK, self is a receiver, but it's allowed.
  8. other = Person.new
  9. other.say "hello" # Error, other is a receiver
  10. end
  11. end

Note that private methods are visible by subclasses:

  1. class Employee < Person
  2. def say_bye
  3. say "bye" # OK
  4. end
  5. end

Private types

Private types can only be referenced inside the namespace where they are defined, and never be fully qualified.

  1. class Foo
  2. private class Bar
  3. end
  4. Bar # OK
  5. Foo::Bar # Error
  6. end
  7. Foo::Bar # Error

private can be used with class, module, lib, enum, alias and constants:

  1. class Foo
  2. private ONE = 1
  3. ONE # => 1
  4. end
  5. Foo::ONE # Error

Protected methods

A protected method can only be invoked on:

  1. instances of the same type as the current type
  2. instances in the same namespace (class, struct, module, etc.) as the current type
  1. # Example of 1
  2. class Person
  3. protected def say(message)
  4. puts message
  5. end
  6. def say_hello
  7. say "hello" # OK, implicit self is a Person
  8. self.say "hello" # OK, self is a Person
  9. other = Person.new "Other"
  10. other.say "hello" # OK, other is a Person
  11. end
  12. end
  13. class Animal
  14. def make_a_person_talk
  15. person = Person.new
  16. person.say "hello" # Error: person is a Person but current type is an Animal
  17. end
  18. end
  19. one_more = Person.new "One more"
  20. one_more.say "hello" # Error: one_more is a Person but current type is the Program
  21. # Example of 2
  22. module Namespace
  23. class Foo
  24. protected def foo
  25. puts "Hello"
  26. end
  27. end
  28. class Bar
  29. def bar
  30. # Works, because Foo and Bar are under Namespace
  31. Foo.new.foo
  32. end
  33. end
  34. end
  35. Namespace::Bar.new.bar

A protected method can only be invoked from the scope of its class or its descendants. That includes the class scope and bodies of class methods and instance methods of the same type the protected method is defined on, as well as all types including or inherinting that type and all types in that namespace.

  1. class Parent
  2. protected def self.protected_method
  3. end
  4. Parent.protected_method # OK
  5. def instance_method
  6. Parent.protected_method # OK
  7. end
  8. def self.class_method
  9. Parent.protected_method # OK
  10. end
  11. end
  12. class Child < Parent
  13. Parent.protected_method # OK
  14. def instance_method
  15. Parent.protected_method # OK
  16. end
  17. def self.class_method
  18. Parent.protected_method # OK
  19. end
  20. end
  21. class Parent::Sub
  22. Parent.protected_method # OK
  23. def instance_method
  24. Parent.protected_method # OK
  25. end
  26. def self.class_method
  27. Parent.protected_method # OK
  28. end
  29. end

Private top-level methods

A private top-level method is only visible in the current file.

!!! example “one.cr”

  1. ```crystal
  2. private def greet
  3. puts "Hello"
  4. end
  5. greet # => "Hello"
  6. ```

!!! example “two.cr”

  1. ```crystal
  2. require "./one"
  3. greet # undefined local variable or method 'greet'
  4. ```

This allows you to define helper methods in a file that will only be known in that file.

Private top-level types

A private top-level type is only visible in the current file.

!!! example “one.cr”

  1. ```crystal
  2. private class Greeter
  3. def self.greet
  4. "Hello"
  5. end
  6. end
  7. Greeter.greet # => "Hello"
  8. ```

!!! example “two.cr”

  1. ```crystal
  2. require "./one"
  3. Greeter.greet # undefined constant 'Greeter'
  4. ```