Is creating a TArray of custom structs without UPROPERTY() macro ok?

I’m trying to implement a Boid simulation of pigeons. For the calculations I’m using a custom FPigeon struct:

#pragma once

#include "CoreMinimal.h"

struct FPigeon
{
	FPigeon(const FVector& Position) : Position(Position), Velocity(FVector(0.0f)) {}
	FVector Position;
	FVector Velocity;
	FTransform GenerateTransform() const;
};

I didn’t mark it as USTRUCT since I will be using it only in internal calculations.
I run the simulation from an Actor class which has a TArray of these structs declared like this:

	TArray<FPigeon> Pigeons;

I initialize this array like this:

void ASimulationRunner::BeginPlay()
{
	Super::BeginPlay();
	Pigeons.Reserve(InitialPigeonCount);
	for (int i = 0; i < InitialPigeonCount; i++)
		Pigeons.Add(FPigeon(FVector(i)));
}

I have three questions related to this:
Is it okay to have a struct which isn’t marked with USTRUCT?
Is it okay to use TArray to store these structs?
Since the TArray isn’t marked as UPROPERTY will it get deallocated correctly?

Is there a better way to implement this?

Is it okay to have a struct which isn’t marked with USTRUCT?
Is it okay to use TArray to store these structs?

Yes.

USTRUCT() in this case is for interacting with editor\reflection system, and since you don’t need it in particular case - it’s ok.

Same goes for TArray<>, it’s a general purpose dynamic array, which supports reflection, but not limited to it.

Since the TArray isn’t marked as UPROPERTY will it get deallocated correctly?

Yes, in first place it’s a general purpose array. At very least - you can define arrays in the middle of function implementation like

void ASimulationRunner::BeginPlay()
{
	Super::BeginPlay();
	TArray<FPigeon> Pigeons2;
	Pigeons2.Reserve(InitialPigeonCount);
	for (int i = 0; i < InitialPigeonCount; i++)
		Pigeons2.Add(FPigeon(FVector(i)));
}

and it will work properly.

Do note though that it’s answer for structs and non-uobject objects. For uobject pointers you have to define UPROPERTY() over them for them not being garbage collected right on spawn.

Awesome, thanks!
I have one more question related to TArrays. I have a method when I create a new one and return it by value like this:

TArray<FTransform> ASimulationRunner::GetPigeonTransforms() const
{
	TArray<FTransform> PigeonTransforms;
	PigeonTransforms.Reserve((*CurrentPigeons).Num());

	for (int i = 0; i < (*CurrentPigeons).Num(); i++)
		PigeonTransforms.Add((*CurrentPigeons)[i].GenerateTransform());

	return PigeonTransforms;
}

From what I know about C++ this should be ok due to RVO, but is it the same in Unreal Engine? Or should I create the array outside of the function and pass it by reference?