Hey @jahh,
Iâm glad you find my explanations helpful 
If youâre defining an array state variable, you need to give it a name (an identifier) otherwise it canât be referenced. In Solidity there are 2 pieces of information that we must always have to define a variable:
- data type
- identifier (name)
// e.g. we can't define a variable as just
uint; // => ERROR
// but we can have
uint myNumber;
With an array, we need to define what data type the array will contain. All of the elements in the array must have the same data type
// e.g
uint[] myArray; // this array will contain unsigned integers
/* If we want our array to contain values which are struct instances,
we define that with the struct name e.g. */
struct Country {
string name;
string capitalCity;
uint populationMillions;
bool visited;
}
// But you still have to give your variable an identifier (a name)
Country[] myArray;
// You can't just define it as...
Country[]; // => ERROR
So, because your struct is called Person
you need to define your array of Person
instances with data type Person[]
and also give it a name:
Person[] people;
// but you don't have to call it people e.g.
Person[] employees;
If you want your variable to only hold a single employee, based on the Person
struct, then you would define it as follows:
Person employee;
/* the single employee this variable can hold
could be visually represented as follows */ {name: Bob, age: 25}
If itâs not a state variable (e.g. a local variable within a function) then for elementary data types the same syntax rules apply e.g.
uint num;
function multiplyByTen() public {
uint myLocalNumber = num * 10 ;
num = myLocalNumber;
}
If our local variable needs to store a complex data type (string, array or struct instance) we need to declare the data location as well as the data type and the identifier (name). Again, if we donât give it an identifier, we canât then reference it to either assign a value to it, or to access its value e.g.
// assumes the same Country struct from the example above
Country[] myArray;
function addCountry(
string memory _name,
string memory _capital,
uint _pop,
bool _visited
) public {
Country memory newCountry = Country(_name, _capital, _pop, _visited);
myArray.push(newCountry);
}
An alternative to this code is:
Country memory newCountry;
newCountry.name = _name;
newCountry.capitalCity = _capital;
newCountry.populationMillions = _pop;
newCountry.visited = _visited;
myArray.push(newCountry);
One of the place you will see (or can use) the data type without the identifier is when we define the data type of the values returned from a function, inâreturns()
e.g.
function getCountryInfo(uint _index) public view
returns(string memory, string memory, uint, bool) {
return(
myArray[_index].name,
myArray[_index].capitalCity,
myArray[_index].populationMillions,
myArray[_index].visited
);
}
If you want to return the whole struct, or even the whole array of structs, then you could have:
(Remember, before v0.8 you need to activate the ABI coder v2 to be able to do this)
// retrieves one of the country instances from the array
function getCountry(uint _index) public view returns(Country memory) {
return(myArray[_index]);
}
// retrieves the whole array of country instances
function getMyCountries() public view returns(Country[] memory) {
return(myArray);
}
Note, that you could also have given the return values an identifier, but in the examples above this is optional.
Hopefully, this gives you the explanations you need 
Just let me know if anything is still unclear, or if you have any further questions 