Main API:
FieldViews.FieldViewable — Type
FieldViewable(array::AbstractArray{T,N}) :: FieldViewable{T,N,Store}Wrap an array to enable zero-copy field access via properties.
FieldViewable provides a view-like interface for accessing individual fields of structs stored in an array, without copying data. Field access returns FieldView objects that can be indexed and modified in-place, with changes reflected in the original array.
Examples
struct Point{T}
x::T
y::T
z::T
end
points = [Point(1.0, 2.0, 3.0), Point(4.0, 5.0, 6.0)]
fv = FieldViewable(points)
# Access field views
fv.x # Returns FieldView{:x, Float64, ...}
fv.x[1] # Returns 1.0
# Modify in-place (modifies original array)
fv.x[1] = 10.0
points[1].x # Now 10.0
# Take views
slice = view(fv, 1:1)
slice.y[1] = 99.0
points[1].y # Now 99.0Performance Note:
Getting and setting to FieldView vectors is most efficient when the following are satisfied:
- The underlying vector (e.g.
arr) satisfies theIsStridedtrait - The
eltypeof the array (e.g.Data{Int}) is concrete and inline-allocated - The type of the field (e.g.
value::Int) is anisbitstype.
When all three of the above are satisfied, FieldViews can use efficient pointer methods to get and set fields in the array directly, otherwise we must use a slower fallback that loads the entire struct, modify it, and then sets the entire struct back into the array.
See also
FieldView: The view type returned by field property accessfieldmap: Customize field layout for nested structures
FieldViews.FieldView — Type
FieldView{field}(parent::AbstractArray)A view of a specific field across all elements in an array.
FieldView provides element-wise access to a single field of the structs in the parent array. For isbits fields and strided array containers, it uses efficient pointer methods for direct field access. For non-isbits fields, or non-strided arrays it uses a slower fallback which loads the full struct and extracts the field value.
Users typically obtain FieldView objects through property access on FieldViewable:
fv = FieldViewable(points)
x_view = fv.x # Returns a FieldView{:x, ...}Indexing
FieldView supports standard array indexing operations:
fv.x[i]- Get field value at index ifv.x[i] = val- Set field value at index i (modifies parent array)
Examples
struct Data{T}
value::T
weight::Float64
end
arr = [Data(1, 0.5), Data(2, 1.5)]
fv = FieldViewable(arr)
# Access field view
values = fv.value # FieldView{:value, Int64, ...}
values[1] # 1
# Modify through view
fv.weight[2] = 2.0
arr[2].weight # 2.0Performance Note:
Getting and setting to FieldView vectors is most efficient when the following are satisfied:
- The underlying vector (e.g.
arr) satisfies theIsStridedtrait - The
eltypeof the array (e.g.Data{Int}) is concrete and inline-allocated (i.e. not backed by a pointer) - The type of the field (e.g.
value::Int) is anisbitstype.
When all three of the above are satisfied, FieldViews can use efficient pointer methods to get and set fields in the array directly, otherwise we must use a slower fallback that loads the entire struct, modify it, and then sets the entire struct back into the array.
See also
FieldViewable: The view type returned by field property accessfieldmap: Customize field layout for nested structures
Adaptions for custom types:
FieldViews.fieldmap — Function
fieldmap(::Type{T}) :: TupleDefine the field layout for type T to be used by FieldViews.
Add methods to this function to customize how FieldViews accesses fields in your types. This is essential for:
- Flattening nested structures
- Renaming fields
- Exposing only certain fields
Default Behavior
By default, returns fieldnames(T), exposing all fields with their original names.
Return Value
A tuple where each element is one of:
SymbolorInt: Direct field accessPair{Symbol,Symbol}: Nested field (e.g.,:outer => :inner)Pair{Symbol,Renamed}: Nested field with renameRenamed: Renamed direct field
Examples
Flattening nested structures
struct MyType{T}
x::T
rest::@NamedTuple{a::Int, b::Int}
end
function FieldViews.fieldmap(::Type{MyType{T}}) where T
(:x, :rest => :a, :rest => :b)
end
fv = FieldViewable([MyType(1.0, (a=1, b=2))])
fv.a[1] # Access nested field directly, returns 1Renaming fields
struct Foo
data::@NamedTuple{_internal::Int}
end
function FieldViews.fieldmap(::Type{Foo})
(:data => Renamed(:_internal, :public),)
end
fv = FieldViewable([Foo((_internal=42,))])
fv.public[1] # Returns 42See also
Renamed: For field aliasingmappedfieldschema: The processed schema generated fromfieldmap
FieldViews.Renamed — Type
Renamed(actual::Union{Int,Symbol}, alias::Symbol)Specify a field rename in custom field mappings.
Used within fieldmap definitions to expose an internal field under a different name. This is useful when you want to expose a given field using a different name, or access Tuple fields (since their fields are just integers).
Arguments
actual: The real field name or field index in the structalias: The name to expose in theFieldViewableinterface
Examples
struct Foo
data::@NamedTuple{_x::Int, _y::Int}
end
function FieldViews.fieldmap(::Type{Foo})
(:data => Renamed(:_x, :x), :data => Renamed(:_y, :y))
end
fv = FieldViewable([Foo((_x=1, _y=2))])
fv.x[1] # Access via alias 'x', returns 1See also
fieldmap: Define custom field layouts usingRenamed
FieldViews.StridedArrayTrait — Type
StridedArrayTrait(::Type{T}) :: StridedArrayTrait
StridedArrayTrait(x) :: StridedArrayTraitQuery or define whether an array type has strided memory layout.
Returns IsStrided() if the array has constant stride offsets in memory, or Unknown() otherwise. Arrays which support IsStrided() auotmatically get more efficient getindex/setindex! implementations for their isbits fields.
IsStrided arrays must support pointer(v, i) methods, and linear indexing.
Default Behavior
StridedArraytypes returnIsStrided()- Other
AbstractArraytypes returnUnknown()
Extending Support
To opt into the strided interface for a custom array type:
FieldViews.StridedArrayTrait(::Type{<:MyArrayType}) = FieldViews.IsStrided()Examples
StridedArrayTrait(Vector{Int}) # IsStrided()
StridedArrayTrait(view([1,2,3,4], 1:2:4)) # IsStrided()
StridedArrayTrait(view([1,2,3,4], [1,3,4])) # Unknown() - non-contiguous indicesA strided array is one where elements are stored at fixed offsets from each other in memory. For example, [1, 2, 3, 4] is strided, as is view(v, 1:2:4) (every other element), but view(v, [1, 3, 4]) is not strided (arbitrary indices).
See also
sourceFieldViews.IsStrided — Type
IsStrided <: StridedArrayTraitTrait indicating that an array type has strided (constant offset) memory layout.
Used by FieldViews to dispatch on array types that support efficient field access through pointer arithmetic.
See also
StridedArrayTrait: The trait function for querying array layoutUnknown: Trait for non-strided arrays
FieldViews.Unknown — Type
Unknown <: StridedArrayTraitTrait indicating that an array type's memory layout is unknown or non-strided.
For arrays with this trait, FieldViews will fall back on accessing/modifying field elements by loading/storing the entire containing struct, and then using FieldLens!! to manipulate and set the required field. This can be slower in some circumstances.
See also
StridedArrayTrait: The trait function for querying array layoutIsStrided: Trait for strided arrays
Utility
FieldViews.FieldLens!! — Type
FieldLens!!{field}An optic for accessing and modifying a specific field of a struct.
FieldLens!! implements the lens interface from Accessors.jl, providing functional field access, immutable or mutable updates. It is primarily used internally by FieldViews for fallback field access/modification, but can also be used directly with the Accessors.jl API.
The !! in its name is a convention from BangBang.jl which signifies that it will mutate when possible, and perform out-of-place updates when mutation is not possible.
Constructor
FieldLens!!(field::Union{Symbol,Int})
FieldLens!!{field}()Examples
struct Point{T}
x::T
y::T
end
lens = FieldLens!!{:x}()
p = Point(1, 2)
lens(p) # Get: returns 1
using Accessors
set(p, lens, 10) # Set: returns Point(10, 2)mutable struct MPoint{T}
x::T
y::T
end
mp = MPoint(1, 2)
lens(mp) # Get: returns 1
set(mp, lens, 10) # Set: returns Point(10, 2)
lens(mp) # Get: returns 10 now since we mutated the objectSee also
- Accessors.jl documentation for general lens usage
Info
FieldViews.can_use_fast_path — Function
can_use_fast_path(::Type{<:FieldView}) :: BoolDetermine whether a FieldView type can use the optimized pointer-based access path.
Returns true if the FieldView can use efficient pointer arithmetic for direct memory access, or false if it must use the potentially slower fallback path that loads and reconstructs entire structs.
Fast Path Requirements
The fast path is used when ALL of the following conditions are met:
- Strided storage: The underlying array satisfies
StridedArrayTrait(Store) == IsStrided() - Concrete element type: The element type of the underlying storage is concrete
- Inline allocated element type: The element type of the underlying storage is not a pointer-backed type (i.e.
Base.allocatedinline) - Isbits field: The field being accessed is and
isbitstype
When all conditions are met, FieldViews can compute the exact memory address of each field and read/write directly using unsafe_load/unsafe_store!.
Slow-Path Fallback
When any condition is not met, FieldViews uses a fallback that:
- For immutable types: loads the struct, extracts/modifies the field, constructs a new struct
- For mutable types: loads the struct, uses
setfield!to modify in place
This is called "slow" only relative to pointer arithmetic—it still performs comparably to manual struct manipulation.
Even when can_use_fast_path returns false, FieldViews still provides correct and reasonably efficient access. The fast path is an optimization, not a requirement for correctness.
See Also
StridedArrayTrait: Query whether an array has strided memory layoutIsStrided: Trait for strided arraysFieldView: The view type this function analyzes
Dangerous tools
FieldViews.mappedfieldschema — Function
mappedfieldschema(::Type{T}) -> NamedTupleCompute the complete field schema for type T, computed using its fieldmap.
Returns a NamedTuple mapping field names (after renaming) to schema information containing:
lens: An optic for accessing the fieldoffset: Byte offset of the field in memory (forisbitsfields)type: The field's data type
This function processes the output of fieldmap to generate the internal schema used by FieldViews for efficient field access.
Examples
struct Point{T}
x::T
y::T
z::T
end
schema = FieldViews.mappedfieldschema(Point{Float64})
# Returns: (x = (lens=..., offset=0, type=Float64),
# y = (lens=..., offset=8, type=Float64),
# z = (lens=..., offset=16, type=Float64))
schema.x.offset # 0
schema.y.offset # 8
schema.z.type # Float64Implementation Note
This is typically called internally by FieldViews and rarely needs to be called directly by users. Adding methods to mappedfieldschema incorrectly could cause undefined behaviour.
See also
fieldmap: The user-facing API for defining field layouts