How to create invisible label?

I want to return a label from a function that doesn’t affect the page layout. I can’t just return the label directly since this causes a cannot join content with label error. How can I do this?

A solution I found is to attach the label to a metadata block.

[
  #metadata("anything can go here")
  #label(identifier)
]

The label is able to be linked to, and metadata doesn’t affect the layout at all.

Your intentions are not clear and without a MRE I don’t know how you got that error.

This code doesn’t give any error and it returns a label from a function:

// #let function() = <a>
#let function() = {
  return <a>
}

#lorem(20)

Return label: #repr(function()) #function()

#lorem(20)

image

1 Like

The label attachment is briefly explained here. But this is indeed how you attach a custom-named label to a metadata element. For hard-coded label name, you can use <identifier> instead.

1 Like

Making an MRE probably would have lead me to the actual issue I was facing.

I have a function that updates a shared state and returns a label. The output of update must return content, which is attempted to be concatenated with the label. Here’s an MRE for that behavior.

#let s = state("test", 0)
#let fun(id) = {
  s.update(cs => {
    cs += 1
  })
  label(id)
}

#fun("test")
#link(label("test"))[link]

Outputs error: cannot join content with label.

To fix this, I just have to explicitly return the label.

#let s = state("test", 0)
#let fun(id) = {
  s.update(cs => {
    cs += 1
  })
  return label(id)
}

#fun("test")
#link(label("test"))[link]

Looks like I’ll just have to adjust from Rust’s implicit return convention. Thanks for your help!

You have to be careful here. With the code you have now, the state isn’t actually updated, as the state update is not returned from the function and thus not placed anywhere in the document.

What you instead want to do, is attach the label to the state update. However, you can only attach labels to elements when you’re in markup mode, not in code mode, so something like this should do the trick:

#let fun(id) = [
  #s.update(cs => cs + 1)
  #label(id)
]
1 Like

Ah, so update returns content that’s used to determine the value of the state at a given point in the document? That explains a lot. Thanks!

1 Like

In addition to what @Eric said, I want to point out the use of state. From your example, it is apparent that you are just counting natural numbers, therefore you should use counter instead. Also, the .update() method, if the function is passed, expects a new value, but you provided none. For state this will therefore set the new value to none, for counter this will give an error.

Here is what you should use instead:

#let c = counter("test")
#let fun(id) = [#c.step()#label(id)]
#fun("test")
#pagebreak()
#link(label("test"))[link]
#context c.get().first()

image

Although this still looks somewhat strange to do without additional context/information.

1 Like

I appreciate the pointer to using counter, that simplifies some syntax. This was just a toy example, though. In truth, I was working on using a function and state to build a dictionary that contains config information about every single file I’m including.