Struct: access only one element from it

Hi there!
It is not so clear to me how to access a single element of a struct:

mutable struct CREDENTIALS
    PASS::AbstractString
    USER::AbstractString
end 

I would like to access CREDENTIALS.PASS and assign it to a certain value, but only that value and not to both elements of the struct. For example CREDENTIALS.PASS = "hello". Then I would like to use that value, for example println(CREDENTIALS.PASS)

Is this possible in Julia?

Thank you for your help.

You have to instantiate that struct:

creds = CREDENTIALS("pass" , "user")
println(creds.PASS) # prints "pass" 
creds.PASS = "newpass" 
println(creds.PASS) # prints "newpass" 

You probably also don’t want to have an abstractly typed field - it will lead to dynamic dispatch. Either use String or a type parameter instead.

1 Like
mutable struct CREDENTIALS{T<:AbstractString}
    PASS::T
    USER::T

    CREDENTIALS{T}() where T<:AbstractString = new{T}("","")
end

c = CREDENTIALS{String}()
c.PASS = "hello"
@show c.PASS # "hello"
@show c.USER # ""
  • Type parameter T to allow specialization on different kinds of AbstractStrings
  • Inner constructor to allow instantiation of “empty” struct, i.e. e.g. CREDENTIALS{String}()
4 Likes

If the other answers are still too complex for you, this is the simplest way to go:

julia> mutable struct Credentials
           user::String
           pass::String
       end
julia> mrRedCredentials   = Credentials("Red","123xyz")
Credentials("Red", "123xyz")
julia> mrWhiteCredentials = Credentials("White","123abc")
Credentials("White", "123abc")
julia> mrRedPsw = mrRedCredentials.pass
"123xyz"
julia> newPassword = "567xyz"
"567xyz"
julia> mrRedCredentials.pass = newPassword
"567xyz"
julia> mrRedCredentials
Credentials("Red", "567xyz")
1 Like

Thank you for your reply @carstenbauer ! I hope you don’t mind if I ask you a couple of questions, so to catch up?

Could you explain why are you using @show?

Here you define {T<:AbstractString} the type AbstractString in the scope and then you define PASS as T (which would be the abstract type).

CREDENTIALS{T}() where T<:AbstractString = new{T}("","") what does it do :slight_smile:

What about here c = CREDENTIALS{String}()? You make an instance of CREDENTIALS as type String. Whay did you change AbstractString to String?

Thank you very much!

Thank you very much for your reply @Sukera !

Not at all.

Why not? :grinning: You can also use println if you want.

Well, the crucial(!) thing is that T is a type parameter which is free (only contraint in that it must be a subtype of AbstractString) but fixed for a concrete instance of the struct. In other words, I can create CREDENTIALS{String}, CREDENTIALS{SubString}, etc. but for all of them individually, the type parameter T is fixed and isconcrete. It is part of the type and thus the compiler can specialize on it (make the code efficient by utilizing the fact that it knows exactly which concrete type is present).

It defines an inner constructor (inner because it’s inside of the struct definition), that is a special function that allows you to actually create objects of type CREDENTIALS{T}. By default, when you define a struct, Julia automatically creates a constructor which takes as input arguments the values that the fields of your struct should take. You must specify all of them explicitly! The inner constructor that I define basically says that you may also create an object with CREDENTIALS{String}(), i.e. without input arguments. In that sense, it just specifies a default for the field values if the user doesn’t provide them. (The function new{T}(args...) is a special function that only exists in inner constructors and creates an object of the type you’re defining the constructor for.)

Objects have concrete types. In your opening post you defined a type CREDENTIALS (without a type parameter) which, despite the fact that the fields where of type AbstractString, is concrete (you can check by using isconcrete). However, a type with fields of an abstract type are bad for performance since the information which kind of concrete string, i.e. <:AbstractString, is actually stored in the object is not available to the compiler. Hence it must produce code that works for objects of arbitrary subtypes of AbstractString, which is inefficient. My parametric type CREDENTIALS{T<:AbstractString} on the other hand gives you the freedom to create credential objects for arbitrary string types (e.g. CREDENTIALS{String}() or CREDENTIALS{SubString}, etc.) but, at the same time, gives the compiler the information for an particular object to reason about the type of the string that is actually stored inside of the credential object (e.g. String or SubString). It gives you flexibility while still being specific for a particular object. Best of both worlds :slightly_smiling_face:

Hope this helps.

4 Likes

Thank you so much @carstenbauer ! I truly appreciate your help!
I see now the magic in what you did. Defining a custom type is something new to me and I should definitevely start considering this as the way to go, to get more juice from the compiler.
:smiley: