Need help understanding creation and usage of Structs

I created a struct and get an error while creating an instance of it. I have tried reading the Julia manual and other material but am unable to resolve the error.

Updated code:
In binaryheap.jl →

mutable struct BinaryHeap{T<:Real}
    htype::string
    heap::Vector{T}
    BinaryHeap{T}(heaptype="min") where T = (heaptype != "min" || heaptype != "max") ? error("Invalid heap type") : new{T}(heaptype, Vector{T}(undef, 0))
end

BinaryHeap{T}() where T = BinaryHeap{T}("min", Vector{T}(undef, 0))
BinaryHeap{T}(v::Vector{T}) where T = BinaryHeap{T}("min", v)

In tests.jl →

module algods
    include("binaryheap.jl")

    export BinaryHeap
    export push!, min, max
end

using algods

function test_heap()
    minheap = algods.BinaryHeap{Integer}("min")

    algods.push!(minheap, 10)
    algods.push!(minheap, 8)
    algods.push!(minheap, 12)

    min(minheap)
end

test_heap()

I get the following error when I run this. Please help me understand how I go about this and what the underlying idea is. Thank you.

WARNING: replacing module algods.
ERROR: LoadError: LoadError: TypeError: in BinaryHeap, in type definition, expected Type, got a value of type typeof(string)
Stacktrace:
 [1] top-level scope
   @ ~/projects/algods/binaryheap.jl:1
 [2] include(mod::Module, _path::String)
   @ Base ./Base.jl:386
 [3] include(x::String)

The constructors using new must be inside the struct. (I didn’t test to see if that is the only problem)

Yet, they are most commonly used if there is an ambiguity with the default constructor, which is not the case there. It is more common to use, outside the struct, things like

struct MyType{T}
  x::T
  y::T
end
MyType(x::T) where T = MyType{T}(x,2*x)
1 Like

Thank you. Making this change doesn’t fix the error.

Post the updated code such that it is easier to help.

Edited the original topic with the updated code. Please take a look. Thank you again.

Missing the {T} there?

1 Like

Thank you for your patience. That didn’t work either.

I think you wanted htype::String, lower-case string is a function not a type.

Also, heaptype != "min" || heaptype != "max" is precisely false, maybe you wanted &&.

Then it runs:

julia> mutable struct BinaryHeap{T<:Real}
           htype::String
           heap::Vector{T}
           BinaryHeap{T}(heaptype="min") where T = (heaptype != "min" && heaptype != "max") ? error("Invalid heap type") : new(heaptype, Vector{T}(undef, 0))
       end

julia> BinaryHeap{Int}()  # now runs
BinaryHeap{Int64}("min", Int64[])

julia> methods(BinaryHeap{Int})  # default has already made a method with 0 args
# 2 methods for type constructor:
[1] BinaryHeap{T}() where T in Main at REPL[8]:4
[2] BinaryHeap{T}(heaptype) where T in Main at REPL[8]:4

julia> methods(BinaryHeap)
# 0 methods for type constructor:

But the point of inner constructors is to make sure there is no other way to make the struct, except through them. Your last method tries to do that, and it fails. It’s trying to call what would be one of the default constructors had you not provided an inner constructor. It could instead construct & then mutate, but probably there are better ways.

julia> BinaryHeap{T}(v::Vector{T}) where T = BinaryHeap{T}("min", v)

julia> BinaryHeap{Int}([1,2,3])
ERROR: MethodError: no method matching BinaryHeap{Int64}(::String, ::Vector{Int64})
4 Likes

Thank you. That fixed the entire problem.

More on style, it’s more Julian to use Symbol type for htype:

mutable struct BinaryHeap{T<:Real}
    htype::Symbol
    heap::Vector{T}
    BinaryHeap{T}(heaptype=:min) where T = heaptype in (:min, :max) ? new{T}(heaptype, Vector{T}(undef, 0)) : error("Invalid heap type") 
end

The idea is that strings must be used where you need sequences of characters, and symbols where you need “atomic” identifiers.