Ruby Class vs Struct

后端 未结 5 814
滥情空心
滥情空心 2021-02-02 08:48

I have seen codebases using Structs to wrap around attributes and behavior inside a class. What is the difference between a Ruby Class and a Struct? And when should one be used

5条回答
  •  北荒
    北荒 (楼主)
    2021-02-02 09:19

    To add to the other answers, there are some things you can not do with a Struct, and some than you can.

    For example, you can not create a Struct with no arguments:

    Bar = Struct.new
    => ArgumentError: wrong number of arguments (given 0, expected 1+)
    
    Bar = Struct.new(:bar)
    bar = Bar.new(nil)
    bar.class
    => Bar
    

    However, a class will let you do that:

    class Foo; end
    foo = Foo.new
    foo.class
    => Foo
    

    You can not set a default value for Struct arguments:

    Bar = Struct.new(bar: 'default')
    => ArgumentError: unknown keyword: bar
    
    Bar = Struct.new(bar = 'default')
    => NameError: identifier default needs to be constant
    

    But you can do it with a class, either passing a hash, were the arguments can be in any order or even missing:

    class Bar
      attr_reader :bar, :rab
      def initialize(bar: 'default', rab:)
        @bar = bar
        @rab = rab
      end
    end
    
    bar = Bar.new(rab: 'mandatory')
    bar.rab
    => 'mandatory'
    bar.bar
    => 'default'
    
    bar = Bar.new(rab: 'mandatory', bar: 'custom_value')
    bar.rab
    => 'mandatory'
    bar.bar
    => 'custom_value'
    

    or passing the values directly, were the arguments should be given in the same order, with the defaulted ones always at the end:

    class Bar
      attr_reader :rab, :bar
      def initialize(rab, bar = 'default')
        @rab = rab
        @bar = bar
      end
    end
    
    bar = Bar.new('mandatory')
    bar.rab
    => 'mandatory'
    bar.bar
    => 'default'
    
    bar = Bar.new('mandatory', 'custom_value')
    bar.rab
    => 'mandatory'
    bar.bar
    => 'custom_value'
    

    You can not do any of that with Structs, unless you set default values for your arguments in this super verbose way:

    A = Struct.new(:a, :b, :c) do
      def initialize(a:, b: 2, c: 3)
        super(a, b, c)
      end
    end
    

    (example taken from this answer)

    You can define methods in a Struct:

    Foo = Struct.new(:foo) do
      def method(argument)
        # do something with argument
        end
      end
    end
    

    Structs can be useful to create data objects, like the point example mentioned in one of the answers.

    I sometimes use them to create fakes and mocks in tests in a simple way. Sometimes RSpec allow(foo).to receive(:blah) etc. can get a bit too verbose and using a Struct is much simple.

提交回复
热议问题