Unexpected : isstructtype( Vector{Float64}) => true

According to the help, it should not happen…

isstructtype(T) -> Bool
Determine whether type T was declared as a struct type (i.e. using the struct or mutable struct keyword).
  1. Is this a bug? Or am I misunderstanding something ?
  2. What is the proper way to detect if a variable (more precisely its type) is a struct ? In a way that does not also catches simple vectors…

Thanks!

2 Likes

What do you need this information for? (XY problem?

2 Likes

Looks like a bug to me, either in the documentation or implementation. If that function is supposed to return true there, the documentation doesn’t say that.

The implementation is

function isstructtype(@nospecialize(t::Type))
    @_pure_meta
    t = unwrap_unionall(t)
    # TODO: what to do for `Union`?
    isa(t, DataType) || return false
    hasfield = !isdefined(t, :types) || !isempty(t.types)
    return hasfield || (t.size == 0 && !isabstracttype(t))
end

and isdefined(Vector{Int}, :types) returns true.

I seem to remember reading in the docs not to use isstructtype because a lot of things are struct types in Julia which don’t intuitively seem structy.

1 Like

isstructtype differentiates a type from being a primitive type or abstract type. It is not about differentiating types from arrays.

An array in Julia is a struct type because it is a struct containing a length, a capacity and memory buffer.

5 Likes
  1. For bootstrapping reasons arrays, including vectors, are implemented in C but are semantically structs. See https://github.com/JuliaLang/julia/blob/3d87815b71f7e1a520a50fcc71b50e7b1c155933/base/boot.jl#L3 and https://github.com/JuliaLang/julia/blob/3d87815b71f7e1a520a50fcc71b50e7b1c155933/base/boot.jl#L55.

  2. Maybe

mystructtest(x::AbstractArray) = false
mystructtest(x) = isstructtype(typeof(x))

but it really depends on what you need it for.

1 Like

Note that even if they’d be implemented in pure julia, they’d still be structs - one hypothetical version could look like

struct NativeArray{T,N}
   length::Int
   data::Ptr{T}
end

(you wouldn’t use this of course, since the data is no longer stored inline and for other reasons).

Containers generally can’t be anything other than structs - having them as primitive types is usually not an option, due to their variable size & different meanings of parts of their bits (i.e. fields).

1 Like

Can someone propose a change for the docs so that it’s more accurate?

What about its docs is inaccurate?

It says that Vector{Float64} is using the struct or mutable struct keyword, but it isn’t.

That sounds more like a technicality than useful documentation… E.g. if it were to say something like

Determine whether objects of type T behave no different from structs (i.e. objects declared using the struct or mutable struct keywords).

I’d bet people would be confused about what other kinds of objects aside from structs & primitive types there are, as that would seemingly imply a third option when there isn’t.

Arrays and vectors are, for all intents and purposes, structs and should be treated as such. How that is checked internally is not relevant and can’t be reduced to how it was written in code anyway - you can always create structs & objects manually using the C API of the runtime when e.g. embedding julia.

1 Like

The argument could be made that isprimitive would have been a more self-explanatory function.

That wouldn’t cover abstract type though. You need all three, isprimitivetype, isabstracttype and isstructtype (and we do have all three).

Abstract types are not values at all and don’t have anything associated with them save for their sub/supertype relationships.

Primitive types are values of “plain bits” and no seperate interpretation (=fields) of a part of them, i.e. they are not composite.

Struct types are values composed of one or more values of some type (primitive or again struct type), i.e. the bits encompassing all of their value have a seperate interpretation apart from being part of the wrapper (they have fields).

Arrays only fit the last definition, as they contain a length & its data (and some other bits) in their (unexposed) fields. How else would you check for whether something is a struct type except for checking whether it has fields? That’s the distinguishing feature after all.

1 Like

!isprimitivetype(T) && !isabstracttype(T) ?